Compare commits
60 Commits
b52c09a176
...
v0.6.1
Author | SHA1 | Date | |
---|---|---|---|
ab41702f6c | |||
2bbf4dd1fd | |||
8909b4a3a8 | |||
f8d776c028 | |||
2154e253ad | |||
df83430c35 | |||
d59a713a8c | |||
5e48d8e8d1 | |||
4f95d3fe16 | |||
ce3185842a | |||
e1d19fd9e5 | |||
3931a4084c | |||
1a492e4eff | |||
58a13eb3cc | |||
d5124829de | |||
37658869e4 | |||
24a43ff37d | |||
16cf055834 | |||
ef0b913063 | |||
05909919e2 | |||
3642c5ac07 | |||
6cee604448 | |||
89d20f4c42 | |||
182b367811 | |||
a2bb09cfbd | |||
b981b5f895 | |||
9dc2e8a59a | |||
1dc05e47cf | |||
21cc20ee63 | |||
491c41b239 | |||
47658a72ae | |||
8b0a4d7979 | |||
9ee7f6baf1 | |||
ecbc9c2d82 | |||
bf90543ad8 | |||
6a5676f916 | |||
75e9d756d2 | |||
ee161b149b | |||
0cb7b4bfc8 | |||
4a49a17b6a | |||
741ccaacae | |||
19f4300440 | |||
954c7a8bdb | |||
626724fe87 | |||
42bf01656e | |||
51293baaae | |||
1d1398a9cd | |||
7d199282d0 | |||
b56a5ed5c6 | |||
201b63c2f1 | |||
b2bd0c9a21 | |||
8502afdc9a | |||
cb541cb6e6 | |||
403e7723d2 | |||
8fbce03031 | |||
b32a935150 | |||
337bfa89d9 | |||
f886888ccc | |||
4dd036babd | |||
b6fd62f8ca |
@@ -65,22 +65,27 @@ namespace Elwig {
|
||||
MainDispatcher = Dispatcher;
|
||||
Scales = Array.Empty<IScale>();
|
||||
CurrentApp = this;
|
||||
OverrideCulture();
|
||||
}
|
||||
|
||||
protected override async void OnStartup(StartupEventArgs evt) {
|
||||
var locale = new CultureInfo("de-AT");
|
||||
locale.NumberFormat.CurrencyGroupSeparator = "\u202f";
|
||||
locale.NumberFormat.NumberGroupSeparator = "\u202f";
|
||||
locale.NumberFormat.PercentGroupSeparator = "\u202f";
|
||||
private static void OverrideCulture() {
|
||||
var locale = new CultureInfo("de-AT", false);
|
||||
locale.NumberFormat.CurrencyGroupSeparator = Utils.GroupSeparator;
|
||||
locale.NumberFormat.NumberGroupSeparator = Utils.GroupSeparator;
|
||||
locale.NumberFormat.PercentGroupSeparator = Utils.GroupSeparator;
|
||||
CultureInfo.CurrentCulture = locale;
|
||||
CultureInfo.CurrentUICulture = locale;
|
||||
Thread.CurrentThread.CurrentCulture = locale;
|
||||
Thread.CurrentThread.CurrentUICulture = locale;
|
||||
CultureInfo.DefaultThreadCurrentCulture = locale;
|
||||
CultureInfo.DefaultThreadCurrentUICulture = locale;
|
||||
FrameworkElement.LanguageProperty.OverrideMetadata(
|
||||
typeof(FrameworkElement),
|
||||
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.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";
|
||||
|
||||
try {
|
||||
|
@@ -12,7 +12,7 @@ namespace Elwig.Dialogs {
|
||||
InitializeComponent();
|
||||
TextLsNr.Text = lsnr;
|
||||
TextMember.Text = name;
|
||||
TextWeight.Text = $"{weight:N0}\u202fkg";
|
||||
TextWeight.Text = $"{weight:N0}{Utils.UnitSeparator}kg";
|
||||
}
|
||||
|
||||
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using Elwig.Helpers;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
@@ -14,7 +13,7 @@ namespace Elwig.Dialogs {
|
||||
|
||||
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
|
||||
DialogResult = true;
|
||||
Price = double.Parse(PriceInput.Text.Replace("\u202f", ""));
|
||||
Price = double.Parse(PriceInput.Text.Replace(Utils.GroupSeparator, ""));
|
||||
Close();
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<UseWPF>true</UseWPF>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
||||
<Version>0.5.1</Version>
|
||||
<Version>0.6.1</Version>
|
||||
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -25,14 +25,14 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.1" />
|
||||
<PackageReference Include="LinqKit" Version="1.2.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.25" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.26" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.1" />
|
||||
<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="ScottPlot.WPF" Version="4.1.68" />
|
||||
<PackageReference Include="ScottPlot.WPF" Version="5.0.19" />
|
||||
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
@@ -13,7 +13,6 @@ namespace Elwig.Helpers.Billing {
|
||||
|
||||
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; }
|
||||
@@ -24,11 +23,7 @@ namespace Elwig.Helpers.Billing {
|
||||
}
|
||||
|
||||
public readonly JsonObject Data;
|
||||
|
||||
private readonly CalculationMode Mode;
|
||||
private readonly Dictionary<int, Curve> Curves;
|
||||
private readonly Dictionary<string, Curve> PaymentData;
|
||||
private readonly Dictionary<string, Curve> QualityData;
|
||||
public readonly CalculationMode Mode;
|
||||
|
||||
public bool ConsiderDelieryModifiers {
|
||||
get => GetConsider("consider_delivery_modifiers");
|
||||
@@ -61,18 +56,13 @@ namespace Elwig.Helpers.Billing {
|
||||
}
|
||||
}
|
||||
|
||||
public BillingData(JsonObject data, IEnumerable<string> attributeVariants) {
|
||||
if (attributeVariants.Any(e => e.Any(c => c < 'A' || c > 'Z')))
|
||||
throw new ArgumentException("Invalid attributeVariants");
|
||||
public BillingData(JsonObject data) {
|
||||
Data = data;
|
||||
var mode = Data["mode"]?.GetValue<string>();
|
||||
Mode = (mode == "elwig") ? CalculationMode.Elwig : CalculationMode.WgMaster;
|
||||
Curves = GetCurves(Data, Mode);
|
||||
PaymentData = GetPaymentData(attributeVariants);
|
||||
QualityData = GetQualityData(attributeVariants);
|
||||
}
|
||||
|
||||
public static JsonObject ParseJson(string json) {
|
||||
protected static JsonObject ParseJson(string json) {
|
||||
if (Schema == null) throw new InvalidOperationException("Schema has to be initialized first");
|
||||
try {
|
||||
var errors = Schema.Validate(json);
|
||||
@@ -84,11 +74,19 @@ namespace Elwig.Helpers.Billing {
|
||||
}
|
||||
|
||||
public static BillingData FromJson(string json) {
|
||||
return FromJson(json, []);
|
||||
return new(ParseJson(json));
|
||||
}
|
||||
|
||||
public static BillingData FromJson(string json, IEnumerable<string> attributeVariants) {
|
||||
return new(ParseJson(json), attributeVariants);
|
||||
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) {
|
||||
@@ -119,13 +117,14 @@ namespace Elwig.Helpers.Billing {
|
||||
return dict;
|
||||
}
|
||||
|
||||
public static Dictionary<int, Curve> GetCurves(JsonObject data, CalculationMode mode) {
|
||||
protected Dictionary<int, Curve> GetCurves() {
|
||||
var dict = new Dictionary<int, Curve>();
|
||||
var curves = data[mode == CalculationMode.Elwig ? "curves" : "Kurven"]?.AsArray() ?? throw new InvalidOperationException();
|
||||
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;
|
||||
double quw = cMode == CurveMode.Oe ? 73 : 15;
|
||||
|
||||
Dictionary<double, decimal> c1;
|
||||
Dictionary<double, decimal>? c2 = null;
|
||||
@@ -133,7 +132,7 @@ namespace Elwig.Helpers.Billing {
|
||||
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 } };
|
||||
c1 = new() { { quw, v } };
|
||||
} else {
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
@@ -141,114 +140,242 @@ namespace Elwig.Helpers.Billing {
|
||||
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);
|
||||
var splitVal = GetCurveValueAt(c1, quw);
|
||||
c2 = c1.ToDictionary(e => e.Key, e => e.Value + (e.Key >= quw ? v : 0));
|
||||
c2[quw] = splitVal + v;
|
||||
c2[Math.BitDecrement(quw)] = splitVal;
|
||||
}
|
||||
dict.Add(id, new(cMode, c1, c2));
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
private Dictionary<string, Curve> GetData(JsonObject data, IEnumerable<string> attributeVariants) {
|
||||
Dictionary<string, Curve> dict;
|
||||
protected static Dictionary<string, JsonValue> GetSelection(JsonNode value, IEnumerable<string> attributeVariants) {
|
||||
if (value is JsonValue flatRate) {
|
||||
return attributeVariants.ToDictionary(e => e, _ => flatRate);
|
||||
} if (value is not JsonObject data) {
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
Dictionary<string, JsonValue> dict;
|
||||
if (data["default"] is JsonValue def) {
|
||||
var c = LookupCurve(def);
|
||||
dict = attributeVariants.ToDictionary(e => e, _ => c);
|
||||
dict = attributeVariants.ToDictionary(e => e, _ => def);
|
||||
} 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);
|
||||
var others = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length > 2 && p.Key != "default");
|
||||
foreach (var (idx, v) in variants) {
|
||||
var curve = LookupCurve(v?.AsValue() ?? throw new InvalidOperationException());
|
||||
var curve = 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());
|
||||
var curve = 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());
|
||||
var curve = v?.AsValue() ?? throw new InvalidOperationException();
|
||||
dict[idx.Replace("/", "")] = curve;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public Dictionary<string, Curve> GetPaymentData(IEnumerable<string> attributeVariants) {
|
||||
var p = Data[Mode == CalculationMode.Elwig ? "payment" : "AuszahlungSorten"];
|
||||
if (p is JsonValue val) {
|
||||
var c = LookupCurve(val);
|
||||
return attributeVariants.ToDictionary(e => e, _ => c);
|
||||
}
|
||||
return GetData(p?.AsObject() ?? throw new InvalidOperationException(), attributeVariants);
|
||||
}
|
||||
public static decimal GetCurveValueAt(Dictionary<double, decimal> curve, double key) {
|
||||
if (curve.Count == 1) return curve.First().Value;
|
||||
|
||||
public Dictionary<string, Curve> GetQualityData(IEnumerable<string> attributeVariants) {
|
||||
var q = Data[Mode == CalculationMode.Elwig ? "quality" : "AuszahlungSortenQualitätsstufe"]?.AsObject();
|
||||
Dictionary<string, Curve> dict = [];
|
||||
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(), attributeVariants);
|
||||
}
|
||||
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);
|
||||
var lt = curve.Keys.Where(v => v <= key);
|
||||
var gt = curve.Keys.Where(v => v >= key);
|
||||
if (!lt.Any()) {
|
||||
return d[gt.Min()];
|
||||
return curve[gt.Min()];
|
||||
} else if (!gt.Any()) {
|
||||
return d[lt.Max()];
|
||||
return curve[lt.Max()];
|
||||
}
|
||||
|
||||
var max = lt.Max();
|
||||
var min = gt.Min();
|
||||
if (max == min) return d[r];
|
||||
if (max == min) return curve[key];
|
||||
|
||||
var p1 = ((decimal)r - (decimal)min) / ((decimal)max - (decimal)min);
|
||||
var p1 = ((decimal)key - (decimal)min) / ((decimal)max - (decimal)min);
|
||||
var p2 = 1 - p1;
|
||||
return d[min] * p2 + d[max] * p1;
|
||||
return curve[min] * p2 + curve[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);
|
||||
protected static JsonObject GraphToJson(Graph graph, string mode) {
|
||||
var x = graph.DataX;
|
||||
var y = graph.DataY;
|
||||
var prec = graph.Precision;
|
||||
|
||||
try {
|
||||
return new JsonObject() {
|
||||
["15kmw"] = Math.Round(y.Distinct().Single(), prec)
|
||||
};
|
||||
} catch { }
|
||||
|
||||
var data = new JsonObject();
|
||||
if (y[0] != y[1]) {
|
||||
data[$"{x[0]}{mode}"] = Math.Round(y[0], prec);
|
||||
}
|
||||
throw new InvalidOperationException();
|
||||
for (int i = 1; i < x.Length - 1; i++) {
|
||||
var d1 = Math.Round(y[i] - y[i - 1], prec);
|
||||
var d2 = Math.Round(y[i + 1] - y[i], prec);
|
||||
if (d1 != d2) {
|
||||
data[$"{x[i]}{mode}"] = Math.Round(y[i], prec);
|
||||
}
|
||||
}
|
||||
if (y[^1] != y[^2]) {
|
||||
data[$"{x[^1]}{mode}"] = Math.Round(y[^1], prec);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public Curve GetCurve(string sortid, string? attrid) {
|
||||
return PaymentData[$"{sortid}{attrid ?? ""}"];
|
||||
protected static JsonNode GraphEntryToJson(GraphEntry entry) {
|
||||
try {
|
||||
if (entry.GebundenFlatBonus == null) {
|
||||
return JsonValue.Create((decimal)entry.DataGraph.DataY.Distinct().Single());
|
||||
}
|
||||
} catch { }
|
||||
|
||||
var curve = new JsonObject {
|
||||
["id"] = entry.Id,
|
||||
["mode"] = entry.Mode.ToString().ToLower(),
|
||||
};
|
||||
|
||||
curve["data"] = GraphToJson(entry.DataGraph, entry.Mode.ToString().ToLower());
|
||||
|
||||
if (entry.GebundenFlatBonus != null) {
|
||||
curve["geb"] = (decimal)entry.GebundenFlatBonus;
|
||||
} else if (entry.GebundenGraph != null) {
|
||||
curve["geb"] = GraphToJson(entry.GebundenGraph, entry.Mode.ToString().ToLower());
|
||||
}
|
||||
|
||||
return curve;
|
||||
}
|
||||
|
||||
public Curve? GetQualityCurve(string qualid, string sortid, string? attrid) {
|
||||
return QualityData.TryGetValue($"{qualid}/{sortid}{attrid ?? ""}", out var curve) ? curve : null;
|
||||
protected static void CollapsePaymentData(JsonObject data, IEnumerable<string> attributeVariants) {
|
||||
Dictionary<string, List<string>> rev1 = [];
|
||||
Dictionary<decimal, List<string>> rev2 = [];
|
||||
foreach (var (k, v) in data) {
|
||||
if (k == "default" || k.StartsWith('/') || !k.Contains('/') || v is not JsonValue val) {
|
||||
continue;
|
||||
} else if (val.TryGetValue<decimal>(out var dec)) {
|
||||
rev2[dec] = rev2.GetValueOrDefault(dec) ?? [];
|
||||
rev2[dec].Add(k);
|
||||
} else if (val.TryGetValue<string>(out var cur)) {
|
||||
rev1[cur] = rev1.GetValueOrDefault(cur) ?? [];
|
||||
rev1[cur].Add(k);
|
||||
}
|
||||
}
|
||||
if (!data.ContainsKey("default")) {
|
||||
foreach (var (v, ks) in rev1) {
|
||||
if (ks.Count >= attributeVariants.Count() / 2.0) {
|
||||
foreach (var k in ks) data.Remove(k);
|
||||
data["default"] = v;
|
||||
CollapsePaymentData(data, attributeVariants);
|
||||
return;
|
||||
}
|
||||
}
|
||||
foreach (var (v, ks) in rev2) {
|
||||
if (ks.Count >= attributeVariants.Count() / 2.0) {
|
||||
foreach (var k in ks) data.Remove(k);
|
||||
data["default"] = v;
|
||||
CollapsePaymentData(data, attributeVariants);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
var attributes = data
|
||||
.Select(e => e.Key)
|
||||
.Where(k => k.Length > 3 && k.Contains('/'))
|
||||
.Select(k => "/" + k.Split('/')[1])
|
||||
.Distinct()
|
||||
.ToList();
|
||||
foreach (var idx in attributes) {
|
||||
var len = attributeVariants.Count(e => e.EndsWith(idx));
|
||||
foreach (var (v, ks) in rev1) {
|
||||
var myKs = ks.Where(k => k.EndsWith(idx)).ToList();
|
||||
if (myKs.Count > 1 && myKs.Count >= len / 2.0) {
|
||||
foreach (var k in myKs) data.Remove(k);
|
||||
data[idx] = v;
|
||||
}
|
||||
}
|
||||
foreach (var (v, ks) in rev2) {
|
||||
var myKs = ks.Where(k => k.EndsWith(idx)).ToList();
|
||||
if (myKs.Count > 1 && myKs.Count >= len / 2.0) {
|
||||
foreach (var k in myKs) data.Remove(k);
|
||||
data[idx] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonObject FromGraphEntries(IEnumerable<GraphEntry> graphEntries, BillingData? origData = null, IEnumerable<string>? attributeVariants = null) {
|
||||
var payment = new JsonObject();
|
||||
var qualityWei = new JsonObject();
|
||||
var curves = new JsonArray();
|
||||
int curveId = 0;
|
||||
foreach (var entry in graphEntries) {
|
||||
var curve = GraphEntryToJson(entry);
|
||||
JsonValue node;
|
||||
if (curve is JsonObject obj) {
|
||||
obj["id"] = ++curveId;
|
||||
node = JsonValue.Create($"curve:{curveId}");
|
||||
curves.Add(obj);
|
||||
} else if (curve is JsonValue val && val.TryGetValue<decimal>(out var flat)) {
|
||||
node = JsonValue.Create(flat);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
foreach (var c in entry.Contracts) {
|
||||
if (entry.Abgewertet) {
|
||||
qualityWei[$"{c.Variety?.SortId}/{c.Attribute?.AttrId}"] = node.DeepClone();
|
||||
} else {
|
||||
payment[$"{c.Variety?.SortId}/{c.Attribute?.AttrId}"] = node.DeepClone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CollapsePaymentData(payment, attributeVariants ?? payment.Select(e => e.Key).ToList());
|
||||
CollapsePaymentData(qualityWei, attributeVariants ?? qualityWei.Select(e => e.Key).ToList());
|
||||
|
||||
var data = new JsonObject {
|
||||
["mode"] = "elwig",
|
||||
["version"] = 1,
|
||||
};
|
||||
|
||||
if (origData?.ConsiderDelieryModifiers == true)
|
||||
data["consider_delivery_modifiers"] = true;
|
||||
if (origData?.ConsiderContractPenalties == true)
|
||||
data["consider_contract_penalties"] = true;
|
||||
if (origData?.ConsiderTotalPenalty == true)
|
||||
data["consider_total_penalty"] = true;
|
||||
if (origData?.ConsiderAutoBusinessShares == true)
|
||||
data["consider_auto_business_shares"] = true;
|
||||
|
||||
if (payment.Count == 0) {
|
||||
data["payment"] = 0;
|
||||
} else if (payment.Count == 1) {
|
||||
data["payment"] = payment.Single().Value?.DeepClone();
|
||||
} else {
|
||||
data["payment"] = payment;
|
||||
}
|
||||
if (qualityWei.Count == 1) {
|
||||
data["quality"] = new JsonObject() {
|
||||
["WEI"] = qualityWei.Single().Value?.DeepClone()
|
||||
};
|
||||
} else if (qualityWei.Count > 1) {
|
||||
data["quality"] = new JsonObject() {
|
||||
["WEI"] = qualityWei
|
||||
};
|
||||
}
|
||||
data["curves"] = curves;
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,19 +10,12 @@ namespace Elwig.Helpers.Billing {
|
||||
|
||||
protected readonly int AvNr;
|
||||
protected readonly PaymentVar PaymentVariant;
|
||||
protected readonly BillingData Data;
|
||||
protected readonly PaymentBillingData Data;
|
||||
|
||||
public BillingVariant(int year, int avnr) : base(year) {
|
||||
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 = BillingData.FromJson(PaymentVariant.Data, attrVariants);
|
||||
Data = PaymentBillingData.FromJson(PaymentVariant.Data, Utils.GetAttributeVarieties(Context, Year));
|
||||
}
|
||||
|
||||
public async Task Calculate() {
|
||||
@@ -146,10 +139,10 @@ namespace Elwig.Helpers.Billing {
|
||||
}
|
||||
|
||||
protected async Task CalculatePrices(SqliteConnection cnx) {
|
||||
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? AttrId, string Discr, int Value, double Oe, double Kmw, string QualId)>();
|
||||
using (var cmd = cnx.CreateCommand()) {
|
||||
cmd.CommandText = $"""
|
||||
SELECT d.year, d.did, d.dpnr, b.bktnr, d.sortid, b.discr, b.value, d.oe, d.kmw, d.qualid
|
||||
SELECT d.year, d.did, d.dpnr, b.bktnr, d.sortid, d.attrid, b.discr, b.value, d.oe, d.kmw, d.qualid
|
||||
FROM delivery_part_bucket b
|
||||
JOIN v_delivery d ON (d.year, d.did, d.dpnr) = (b.year, b.did, b.dpnr)
|
||||
WHERE b.year = {Year}
|
||||
@@ -158,16 +151,18 @@ namespace Elwig.Helpers.Billing {
|
||||
while (await reader.ReadAsync()) {
|
||||
parts.Add((
|
||||
reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetInt32(3),
|
||||
reader.GetString(4), reader.GetString(5), reader.GetInt32(6),
|
||||
reader.GetDouble(7), reader.GetDouble(8), reader.GetString(9)
|
||||
reader.GetString(4), reader.IsDBNull(5) ? null : reader.GetString(5), reader.GetString(6),
|
||||
reader.GetInt32(7), reader.GetDouble(8), reader.GetDouble(9), reader.GetString(10)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
var inserts = new List<(int Year, int DId, int DPNr, int BktNr, long Price, long Amount)>();
|
||||
foreach (var part in parts) {
|
||||
var attrId = (part.Discr == "_" || part.Discr == "") ? null : part.Discr;
|
||||
var price = Data.CalculatePrice(part.SortId, attrId, part.QualId, part.Discr != "_", part.Oe, part.Kmw);
|
||||
var ungeb = part.Discr == "_";
|
||||
var payAttrId = (part.Discr is "" or "_") ? null : part.Discr;
|
||||
var geb = !ungeb && payAttrId == part.AttrId;
|
||||
var price = Data.CalculatePrice(part.SortId, part.AttrId, part.QualId, geb, part.Oe, part.Kmw);
|
||||
var priceL = PaymentVariant.Season.DecToDb(price);
|
||||
inserts.Add((part.Year, part.DId, part.DPNr, part.BktNr, priceL, priceL * part.Value));
|
||||
}
|
||||
|
25
Elwig/Helpers/Billing/ContractSelection.cs
Normal file
25
Elwig/Helpers/Billing/ContractSelection.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Elwig.Models.Entities;
|
||||
using System;
|
||||
|
||||
namespace Elwig.Helpers.Billing {
|
||||
public class ContractSelection : IComparable<ContractSelection> {
|
||||
|
||||
public WineVar? Variety { get; }
|
||||
public WineAttr? Attribute { get; }
|
||||
public string Listing => $"{Variety?.SortId}{Attribute?.AttrId}";
|
||||
public string FullName => $"{Variety?.Name}" + (Variety != null && Attribute != null ? " " : "") + $"{Attribute?.Name}";
|
||||
|
||||
public ContractSelection(WineVar? var, WineAttr? attr) {
|
||||
Variety = var;
|
||||
Attribute = attr;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return Listing;
|
||||
}
|
||||
|
||||
public int CompareTo(ContractSelection? other) {
|
||||
return Listing.CompareTo(other?.Listing);
|
||||
}
|
||||
}
|
||||
}
|
102
Elwig/Helpers/Billing/EditBillingData.cs
Normal file
102
Elwig/Helpers/Billing/EditBillingData.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using Elwig.Models.Entities;
|
||||
using System;
|
||||
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);
|
||||
}
|
||||
|
||||
private (Dictionary<int, Curve>, Dictionary<int, List<string>>) GetGraphEntries(JsonNode root) {
|
||||
Dictionary<int, List<string>> dict1 = [];
|
||||
Dictionary<decimal, List<string>> dict2 = [];
|
||||
if (root 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 (root is JsonValue paymentVal) {
|
||||
if (paymentVal.TryGetValue<decimal>(out var price)) {
|
||||
if (!dict2.ContainsKey(price)) dict2[price] = [];
|
||||
dict2[price].Add("default");
|
||||
} else if (paymentVal.TryGetValue<string>(out var curve)) {
|
||||
var idx = int.Parse(curve.Split(":")[1] ?? "0");
|
||||
if (!dict1.ContainsKey(idx)) dict1[idx] = [];
|
||||
dict1[idx].Add("default");
|
||||
}
|
||||
}
|
||||
|
||||
var virtOffset = dict1.Count > 0 ? dict1.Max(e => e.Key) + 1 : 1;
|
||||
Dictionary<int, Curve> curves = GetCurves();
|
||||
decimal[] virtCurves = [.. dict2.Keys.Order()];
|
||||
for (int i = 0; i < virtCurves.Length; i++) {
|
||||
var idx = virtCurves[i];
|
||||
dict1[i + virtOffset] = dict2[idx];
|
||||
curves[i + virtOffset] = new Curve(CurveMode.Oe, new() { { 73, idx } }, null);
|
||||
}
|
||||
|
||||
Dictionary<int, List<string>> dict3 = curves.ToDictionary(c => c.Key, _ => new List<string>());
|
||||
foreach (var (selector, value) in GetSelection(root, AttributeVariants)) {
|
||||
int? idx = null;
|
||||
if (value.TryGetValue<decimal>(out var val)) {
|
||||
idx = Array.IndexOf(virtCurves, val) + virtOffset;
|
||||
} else if (value.TryGetValue<string>(out var str)) {
|
||||
idx = int.Parse(str.Split(":")[1]);
|
||||
}
|
||||
if (idx != null)
|
||||
dict3[(int)idx].Add(selector);
|
||||
}
|
||||
|
||||
return (curves, dict3);
|
||||
}
|
||||
|
||||
private static List<GraphEntry> CreateGraphEntries(AppDbContext ctx, int precision, Dictionary<int, Curve> curves, Dictionary<int, List<string>> entries) {
|
||||
var vars = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v);
|
||||
var attrs = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a);
|
||||
return entries
|
||||
.Select(e => new GraphEntry(e.Key, precision, curves[e.Key], e.Value
|
||||
.Select(s => new ContractSelection(vars[s[..2]], s.Length > 2 ? attrs[s[2..]] : null))
|
||||
.ToList()))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<GraphEntry> GetPaymentGraphEntries(AppDbContext ctx, Season season) {
|
||||
var root = GetPaymentEntry();
|
||||
var (curves, entries) = GetGraphEntries(root);
|
||||
return CreateGraphEntries(ctx, season.Precision, curves, entries).Where(e => e.Contracts.Count > 0);
|
||||
}
|
||||
|
||||
public IEnumerable<GraphEntry> GetQualityGraphEntries(AppDbContext ctx, Season season, int idOffset = 0) {
|
||||
var root = GetQualityEntry();
|
||||
if (root == null || root["WEI"] is not JsonNode qualityWei)
|
||||
return [];
|
||||
var (curves, entries) = GetGraphEntries(qualityWei);
|
||||
var list = CreateGraphEntries(ctx, season.Precision, curves, entries).Where(e => e.Contracts.Count > 0);
|
||||
foreach (var e in list) {
|
||||
e.Id += idOffset;
|
||||
e.Abgewertet = true;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,119 +1,107 @@
|
||||
using ScottPlot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Elwig.Helpers.Billing {
|
||||
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 readonly int Precision;
|
||||
public double[] DataX { get; set; }
|
||||
public double[] DataY { get; set; }
|
||||
public int MinX { get; set; }
|
||||
public int MaxX { get; set; }
|
||||
|
||||
public Graph(int num, int minX, int maxX) {
|
||||
Type = "oe";
|
||||
Num = num;
|
||||
Contracts = "";
|
||||
public Graph(int precision, int minX, int maxX) {
|
||||
Precision = precision;
|
||||
MinX = minX;
|
||||
MaxX = maxX;
|
||||
|
||||
DataX = DataGen.Range(MinX, MaxX + 1);
|
||||
DataY = DataGen.Zeros(MaxX - MinX + 1);
|
||||
DataX = Enumerable.Range(minX, maxX - minX + 1).Select(n => (double)n).ToArray();
|
||||
DataY = new double[DataX.Length];
|
||||
}
|
||||
|
||||
public Graph(string type, int num, JsonObject graphData, string contracts, int minX, int maxX) {
|
||||
Type = type;
|
||||
Num = num;
|
||||
Contracts = contracts;
|
||||
public Graph(Dictionary<double, decimal> data, int precision, int minX, int maxX) {
|
||||
Precision = precision;
|
||||
MinX = minX;
|
||||
MaxX = maxX;
|
||||
|
||||
DataX = DataGen.Range(MinX, MaxX + 1);
|
||||
DataY = DataGen.Zeros(MaxX - MinX + 1);
|
||||
ParseGraphData(graphData);
|
||||
DataX = Enumerable.Range(minX, maxX - minX + 1).Select(n => (double)n).ToArray();
|
||||
DataY = DataX.Select(i => (double)BillingData.GetCurveValueAt(data, i)).ToArray();
|
||||
}
|
||||
|
||||
public Graph(string type, int num, int minX, int maxX, string contracts, double[] dataX, double[] dataY) {
|
||||
Type = type;
|
||||
Num = num;
|
||||
public Graph(double[] values, int precision, int minX, int maxX) {
|
||||
Precision = precision;
|
||||
MinX = minX;
|
||||
MaxX = maxX;
|
||||
DataX = Enumerable.Range(MinX, MaxX - MinX + 1).Select(i => (double)i).ToArray();
|
||||
DataY = values;
|
||||
}
|
||||
|
||||
private Graph(double[] dataX, double[] dataY, int precision, int minX, int maxX) {
|
||||
Precision = precision;
|
||||
MinX = minX;
|
||||
MaxX = maxX;
|
||||
Contracts = contracts;
|
||||
DataX = dataX;
|
||||
DataY = dataY;
|
||||
}
|
||||
|
||||
private void ParseGraphData(JsonObject graphData) {
|
||||
var GraphPoints = graphData.ToDictionary(p => int.Parse(p.Key[..^2]), p => (double)p.Value?.AsValue());
|
||||
public double GetOechsleAt(int index) {
|
||||
return DataX[index];
|
||||
}
|
||||
|
||||
if (GraphPoints.Keys.Count < 1) {
|
||||
return;
|
||||
}
|
||||
public void SetOechsleAt(int index, double oechsle) {
|
||||
DataX[index] = oechsle;
|
||||
}
|
||||
|
||||
var minKey = GraphPoints.Keys.Order().First();
|
||||
var maxKey = GraphPoints.Keys.OrderDescending().First();
|
||||
public void SetPriceAt(int index, double price) {
|
||||
DataY[index] = price;
|
||||
}
|
||||
|
||||
if (!GraphPoints.ContainsKey(MinX)) {
|
||||
GraphPoints.Add(MinX, GraphPoints.GetValueOrDefault(minKey));
|
||||
}
|
||||
if (!GraphPoints.ContainsKey(MaxX)) {
|
||||
GraphPoints.Add(MaxX, GraphPoints.GetValueOrDefault(maxKey));
|
||||
}
|
||||
public double GetPriceAt(int index) {
|
||||
return DataY[index];
|
||||
}
|
||||
|
||||
var keys = GraphPoints.Keys.Order().ToArray();
|
||||
public double GetPriceAtOe(double oe) {
|
||||
return DataY[Array.IndexOf(DataX, oe)];
|
||||
}
|
||||
|
||||
for (int i = 0; i < keys.Length; i++) {
|
||||
double point1Value = GraphPoints[keys[i]];
|
||||
if (i + 1 < keys.Length) {
|
||||
double point2Value = GraphPoints[keys[i + 1]];
|
||||
if (point1Value == point2Value) {
|
||||
for (int j = keys[i] - MinX; j < keys[i + 1] - MinX; j++) {
|
||||
DataY[j] = point1Value;
|
||||
}
|
||||
} else {
|
||||
int steps = Math.Abs(keys[i + 1] - keys[i]);
|
||||
double step = (point2Value - point1Value) / steps;
|
||||
|
||||
DataY[keys[i] - MinX] = point1Value;
|
||||
DataY[keys[i + 1] - MinX] = point2Value;
|
||||
|
||||
for (int j = keys[i] - MinX; j < keys[i + 1] - MinX - 1; j++) {
|
||||
DataY[j + 1] = Math.Round(DataY[j] + step, 4); // TODO richtig runden
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int j = keys[i] - MinX; j < DataX.Length; j++) {
|
||||
DataY[j] = point1Value;
|
||||
}
|
||||
}
|
||||
private void FlattenGraph(int begin, int end, double value) {
|
||||
for (int i = begin; i <= end; i++) {
|
||||
DataY[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public JsonObject ToJson() {
|
||||
JsonObject graph = new();
|
||||
public void FlattenGraphLeft(int pointIndex) {
|
||||
FlattenGraph(0, pointIndex, DataY[pointIndex]);
|
||||
}
|
||||
|
||||
if (DataY[0] != DataY[1]) {
|
||||
graph.Add(new KeyValuePair<string, JsonNode?>(DataX[0] + Type.ToLower(), Math.Round(DataY[0], 4)));
|
||||
public void FlattenGraphRight(int pointIndex) {
|
||||
FlattenGraph(pointIndex, DataY.Length - 1, DataY[pointIndex]);
|
||||
}
|
||||
|
||||
private void LinearIncreaseGraph(int begin, int end, double inc) {
|
||||
for (int i = begin; i < end; i++) {
|
||||
DataY[i + 1] = Math.Round(DataY[i] + inc, Precision);
|
||||
}
|
||||
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)) {
|
||||
graph.Add(new KeyValuePair<string, JsonNode?>(DataX[i] + Type.ToLower(), Math.Round(DataY[i], 4)));
|
||||
}
|
||||
}
|
||||
|
||||
public void LinearIncreaseGraphToEnd(int begin, double inc) {
|
||||
LinearIncreaseGraph(begin, DataY.Length - 1, inc);
|
||||
}
|
||||
|
||||
public void InterpolateGraph(int firstPoint, int secondPoint) {
|
||||
int steps = Math.Abs(firstPoint - secondPoint);
|
||||
if (firstPoint == -1 || secondPoint == -1 || steps < 2) {
|
||||
return;
|
||||
}
|
||||
if (DataY[^1] != DataY[^2]) {
|
||||
graph.Add(new KeyValuePair<string, JsonNode?>(DataX[^1] + Type.ToLower(), Math.Round(DataY[^1], 4)));
|
||||
var (lowIndex, highIndex) = firstPoint < secondPoint ? (firstPoint, secondPoint) : (secondPoint, firstPoint);
|
||||
double step = (DataY[highIndex] - DataY[lowIndex]) / steps;
|
||||
|
||||
for (int i = lowIndex; i < highIndex - 1; i++) {
|
||||
DataY[i + 1] = Math.Round(DataY[i] + step, Precision);
|
||||
}
|
||||
return graph;
|
||||
}
|
||||
|
||||
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(), Precision, MinX, MaxX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
88
Elwig/Helpers/Billing/GraphEntry.cs
Normal file
88
Elwig/Helpers/Billing/GraphEntry.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Elwig.Helpers.Billing {
|
||||
public class GraphEntry {
|
||||
|
||||
public const int MinX = 50;
|
||||
public const int MinXGeb = 73;
|
||||
public const int MaxX = 140;
|
||||
|
||||
public int Id { get; set; }
|
||||
public BillingData.CurveMode Mode { get; set; }
|
||||
public bool Abgewertet { get; set; }
|
||||
|
||||
public Graph DataGraph { get; set; }
|
||||
public Graph? GebundenGraph { get; set; }
|
||||
public double? GebundenFlatBonus {
|
||||
get {
|
||||
try {
|
||||
var val = GebundenGraph?.DataX.Zip(GebundenGraph.DataY)
|
||||
.Select(e => Math.Round(e.Second - DataGraph.GetPriceAtOe(e.First), Precision))
|
||||
.Distinct()
|
||||
.Single();
|
||||
return (val == 0) ? null : val;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
set {
|
||||
if (value is not double v) return;
|
||||
var values = Enumerable.Range(MinXGeb, MaxX - MinXGeb + 1)
|
||||
.Select(i => Math.Round(DataGraph.GetPriceAtOe(i) + v, Precision))
|
||||
.ToArray();
|
||||
GebundenGraph = new Graph(values, Precision, MinXGeb, MaxX);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ContractSelection> Contracts { get; set; }
|
||||
public string ContractsStringSimple => (Abgewertet ? "Abgew.: " : "") + (Contracts.Count != 0 ? (Contracts.Count >= 25 ? "Restliche Sorten" : string.Join(", ", Contracts.Select(c => c.Listing))) : "-");
|
||||
public string ContractsString => Contracts.Count != 0 ? string.Join("\n", Contracts.Select(c => c.FullName)) : "-";
|
||||
public string ContractsStringChange => (Abgewertet ? "A." : "") + string.Join(",", Contracts.Select(c => c.Listing));
|
||||
private readonly int Precision;
|
||||
|
||||
public GraphEntry(int id, int precision, BillingData.CurveMode mode) {
|
||||
Id = id;
|
||||
Precision = precision;
|
||||
Mode = mode;
|
||||
DataGraph = new Graph(precision, MinX, MaxX); ;
|
||||
Contracts = [];
|
||||
}
|
||||
|
||||
public GraphEntry(int id, int precision, BillingData.CurveMode mode, Dictionary<double, decimal> data, Dictionary<double, decimal>? gebunden) :
|
||||
this(id, precision, mode) {
|
||||
DataGraph = new Graph(data, precision, MinX, MaxX);
|
||||
if (gebunden != null) GebundenGraph = new Graph(gebunden, precision, MinXGeb, MaxX);
|
||||
}
|
||||
|
||||
public GraphEntry(int id, int precision, BillingData.Curve curve, List<ContractSelection> contracts) :
|
||||
this(id, precision, curve.Mode) {
|
||||
DataGraph = new Graph(curve.Normal, precision, MinX, MaxX);
|
||||
if (curve.Gebunden != null)
|
||||
GebundenGraph = new Graph(curve.Gebunden, precision, MinXGeb, MaxX);
|
||||
Contracts = contracts;
|
||||
}
|
||||
|
||||
private GraphEntry(int id, int precision, BillingData.CurveMode mode, Graph dataGraph, Graph? gebundenGraph, List<ContractSelection> contracts) {
|
||||
Id = id;
|
||||
Precision = precision;
|
||||
Mode = mode;
|
||||
DataGraph = dataGraph;
|
||||
GebundenGraph = gebundenGraph;
|
||||
Contracts = contracts;
|
||||
}
|
||||
|
||||
public void AddGebundenGraph() {
|
||||
GebundenGraph ??= new Graph(Precision, MinXGeb, MaxX);
|
||||
}
|
||||
|
||||
public void RemoveGebundenGraph() {
|
||||
GebundenGraph = null;
|
||||
}
|
||||
|
||||
public GraphEntry Copy(int id) {
|
||||
return new GraphEntry(id, Precision, Mode, (Graph)DataGraph.Clone(), (Graph?)GebundenGraph?.Clone(), []);
|
||||
}
|
||||
}
|
||||
}
|
73
Elwig/Helpers/Billing/PaymentBillingData.cs
Normal file
73
Elwig/Helpers/Billing/PaymentBillingData.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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(JsonNode data) {
|
||||
return GetSelection(data, AttributeVariants).ToDictionary(e => e.Key, e => LookupCurve(e.Value));
|
||||
}
|
||||
|
||||
protected Dictionary<string, Curve> GetPaymentData() {
|
||||
return GetData(GetPaymentEntry());
|
||||
}
|
||||
|
||||
protected Dictionary<string, Curve> GetQualityData() {
|
||||
Dictionary<string, Curve> dict = [];
|
||||
var q = GetQualityEntry();
|
||||
if (q == null) return dict;
|
||||
|
||||
foreach (var (qualid, data) in q) {
|
||||
foreach (var (idx, d) in GetData(data ?? throw new InvalidOperationException())) {
|
||||
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);
|
||||
return GetCurveValueAt((gebunden ? curve.Gebunden : null) ?? curve.Normal, curve.Mode == CurveMode.Oe ? oe : kmw);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -24,9 +24,9 @@ namespace Elwig.Helpers {
|
||||
public void Read() {
|
||||
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"];
|
||||
DatabaseLog = log != null ? Utils.GetAbsolutePath(log, App.DataPath) : null;
|
||||
DatabaseLog = log != null ? Path.Combine(App.DataPath, log) : null;
|
||||
Branch = config["general:branch"];
|
||||
Debug = trueValues.Contains(config["general:debug"]?.ToLower());
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Elwig.Helpers {
|
||||
Scales = ScaleList;
|
||||
foreach (var s in scales) {
|
||||
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([
|
||||
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
|
||||
|
@@ -12,6 +12,8 @@ using System.Text;
|
||||
using System.Numerics;
|
||||
using Elwig.Models.Entities;
|
||||
using System.IO;
|
||||
using ScottPlot.TickGenerators.TimeUnits;
|
||||
using Elwig.Helpers.Billing;
|
||||
|
||||
namespace Elwig.Helpers {
|
||||
public static partial class Utils {
|
||||
@@ -48,6 +50,9 @@ namespace Elwig.Helpers {
|
||||
[GeneratedRegex(@"^(.*?) +([0-9].*)$", RegexOptions.Compiled)]
|
||||
private static partial Regex GeneratedAddressRegex();
|
||||
|
||||
public static readonly string GroupSeparator = "\u202F";
|
||||
public static readonly string UnitSeparator = "\u00A0";
|
||||
|
||||
public static readonly KeyValuePair<string, string>[] PhoneNrTypes = [
|
||||
new("landline", "Tel.-Nr. (Festnetz)"),
|
||||
new("mobile", "Tel.-Nr. (mobil)"),
|
||||
@@ -357,8 +362,26 @@ namespace Elwig.Helpers {
|
||||
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;
|
||||
public static List<string> GetAttributeVarieties(AppDbContext ctx, int year, bool withSlash = false) {
|
||||
return ctx.DeliveryParts
|
||||
.Where(d => d.Year == year)
|
||||
.Select(d => $"{d.SortId}{(withSlash ? "/" : "")}{d.AttrId}")
|
||||
.Distinct()
|
||||
.ToList()
|
||||
.Union(ctx.WineVarieties.Select(v => v.SortId))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static List<ContractSelection> GetContractsForYear(AppDbContext ctx, int year) {
|
||||
return ctx.DeliveryParts
|
||||
.Where(p => p.Year == year)
|
||||
.Select(d => new ContractSelection(d.Variant, d.Attribute))
|
||||
.Distinct()
|
||||
.ToList()
|
||||
.Union(ctx.WineVarieties.Select(v => new ContractSelection(v, null)))
|
||||
.DistinctBy(c => c.Listing)
|
||||
.Order()
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -26,6 +26,10 @@ namespace Elwig.Models.Dtos {
|
||||
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) {
|
||||
return (await FromDbSet(table, year))
|
||||
.GroupBy(
|
||||
|
@@ -68,19 +68,32 @@ namespace Elwig.Models.Entities {
|
||||
|
||||
[InverseProperty("Delivery")]
|
||||
public virtual ISet<DeliveryPart> Parts { get; private set; }
|
||||
[NotMapped]
|
||||
public IEnumerable<DeliveryPart> FilteredParts => PartFilter == null ? Parts : Parts.Where(p => PartFilter(p));
|
||||
|
||||
[NotMapped]
|
||||
public Predicate<DeliveryPart>? PartFilter { get; set; }
|
||||
|
||||
public int Weight => Parts.Select(p => p.Weight).Sum();
|
||||
public int FilteredWeight => FilteredParts.Select(p => p.Weight).Sum();
|
||||
|
||||
public IEnumerable<string> SortIds => Parts
|
||||
.GroupBy(p => p.SortId)
|
||||
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
||||
.Select(g => g.Select(p => p.SortId).First());
|
||||
.Select(g => g.Key);
|
||||
public IEnumerable<string> FilteredSortIds => FilteredParts
|
||||
.GroupBy(p => p.SortId)
|
||||
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
||||
.Select(g => g.Key);
|
||||
|
||||
public string SortIdString => string.Join(", ", SortIds);
|
||||
public string FilteredSortIdString => string.Join(", ", FilteredSortIds);
|
||||
|
||||
public double Kmw => Utils.AggregateDeliveryPartsKmw(Parts);
|
||||
public double FilteredKmw => Utils.AggregateDeliveryPartsKmw(FilteredParts);
|
||||
|
||||
public double Oe => Utils.KmwToOe(Kmw);
|
||||
public double FilteredOe => Utils.KmwToOe(FilteredKmw);
|
||||
|
||||
public int SearchScore(IEnumerable<string> keywords) {
|
||||
var list = new string?[] {
|
||||
|
@@ -23,6 +23,13 @@ namespace Elwig.Models.Entities {
|
||||
[Column("fill_lower")]
|
||||
public int FillLower { get; set; }
|
||||
|
||||
public WineAttr() { }
|
||||
|
||||
public WineAttr(string attrId, string name) {
|
||||
AttrId = attrId;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return Name;
|
||||
}
|
||||
|
@@ -21,6 +21,13 @@ namespace Elwig.Models.Entities {
|
||||
public bool IsRed => Type == "R";
|
||||
public bool IsWhite => Type == "W";
|
||||
|
||||
public WineVar() { }
|
||||
|
||||
public WineVar(string sortId, string name) {
|
||||
SortId = sortId;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return Name;
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<PublishDir>bin\Publish</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<_TargetId>Folder</_TargetId>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
@@ -125,6 +125,7 @@ namespace Elwig.Windows {
|
||||
if (old != null) _branches[old] = id;
|
||||
branch.ZwstId = id;
|
||||
branch.Name = BranchNameInput.Text;
|
||||
branch.CountryNum = 40;
|
||||
branch.PostalDestId = (BranchOrtInput.SelectedItem as AT_PlzDest)?.Id;
|
||||
branch.Address = BranchAddressInput.Text;
|
||||
branch.PhoneNr = BranchPhoneNrInput.Text;
|
||||
|
@@ -52,13 +52,13 @@ namespace Elwig.Windows {
|
||||
|
||||
var year = (SeasonList.SelectedItem as Season)?.Year;
|
||||
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) {
|
||||
mod.ModId = old;
|
||||
}
|
||||
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();
|
||||
|
||||
@@ -102,8 +102,9 @@ namespace Elwig.Windows {
|
||||
if (_modList == null || SeasonList.SelectedItem is not Season s) return;
|
||||
_modChanged = true;
|
||||
var idx = (SeasonModifierList.SelectedIndex != -1) ? SeasonModifierList.SelectedIndex + 1 : _modList.Count;
|
||||
var item = Context.CreateProxy<Modifier>();
|
||||
item.Year = s.Year;
|
||||
var item = new Modifier {
|
||||
Year = s.Year
|
||||
};
|
||||
_modList.Insert(idx, item);
|
||||
SeasonModifierList.SelectedIndex = idx;
|
||||
UpdateButtons();
|
||||
|
@@ -264,6 +264,8 @@ namespace Elwig.Windows {
|
||||
ClearInputStates();
|
||||
FillInputs(App.Client);
|
||||
LockInputs();
|
||||
|
||||
await HintContextChange();
|
||||
}
|
||||
|
||||
private void FillInputs(ClientParameters p) {
|
||||
|
@@ -1,15 +1,17 @@
|
||||
<local:AdministrationWindow
|
||||
<local:ContextWindow
|
||||
x:Class="Elwig.Windows.ChartWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Elwig.Windows"
|
||||
xmlns:ctrl="clr-namespace:Elwig.Controls"
|
||||
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
|
||||
xmlns:ScottPlot="clr-namespace:ScottPlot;assembly=ScottPlot.WPF"
|
||||
xmlns:ScottPlot="clr-namespace:ScottPlot.WPF;assembly=ScottPlot.WPF"
|
||||
mc:Ignorable="d"
|
||||
Title="Auszahlung - Elwig" Height="700" Width="1500"
|
||||
Loaded="Window_Loaded">
|
||||
Title="Auszahlung - Elwig" Height="700" Width="1500" MinWidth="1000" MinHeight="500"
|
||||
Loaded="Window_Loaded"
|
||||
Closing="Window_Closing">
|
||||
|
||||
<Window.Resources>
|
||||
<Style TargetType="Label">
|
||||
@@ -43,94 +45,79 @@
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="19"/>
|
||||
<RowDefinition Height="40"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="330"/>
|
||||
<ColumnDefinition Width="300"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="200"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Row="1" Margin="5,0,0,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="42"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="3">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="560"/>
|
||||
<ColumnDefinition Width="100"/>
|
||||
</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"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
|
||||
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"/>
|
||||
<Label Content="Für:" Margin="10,-2,0,0" FontSize="14" Grid.Column="0" VerticalAlignment="Center"/>
|
||||
<xctk:CheckComboBox x:Name="ContractInput" Margin="50,0,0,0" Grid.Column="0"
|
||||
Delimiter=", " AllItemsSelectedContent="Alle" IsEnabled="False" ItemSelectionChanged="ContractInput_Changed"
|
||||
Width="500" Height="25" HorizontalAlignment="Left">
|
||||
<xctk:CheckComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding Variety.Name}" Width="150"/>
|
||||
<TextBlock Text="{Binding Attribute.Name}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</xctk:CheckComboBox.ItemTemplate>
|
||||
</xctk:CheckComboBox>
|
||||
|
||||
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="False" Visibility="Hidden"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
|
||||
Click="SaveButton_Click"/>
|
||||
<Button x:Name="ResetButton" Content="Zurücksetzen" IsEnabled="False" Visibility="Hidden"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
|
||||
Click="ResetButton_Click"/>
|
||||
<Button x:Name="CancelButton" Content="Abbrechen" IsEnabled="False" Visibility="Hidden" IsCancel="True"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
|
||||
Click="CancelButton_Click"/>
|
||||
<CheckBox x:Name="AbgewertetInput" Content="Abgewertet" IsEnabled="False" Checked="AbgewertetInput_Changed" Unchecked="AbgewertetInput_Changed"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,0,0" Grid.Column="1"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" Grid.Column="1">
|
||||
<ScottPlot:WpfPlot x:Name="OechslePricePlot" MouseMove="OechslePricePlot_MouseMove" MouseDown="OechslePricePlot_MouseDown" IsEnabled="False"/>
|
||||
<ListBox x:Name="GraphList" Margin="10,10,35,42" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" SelectionChanged="GraphList_SelectionChanged">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding Id}" Width="30"/>
|
||||
<TextBlock Text="{Binding ContractsStringSimple}" ToolTip="{Binding ContractsString}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="10,5,35,10" 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.RowSpan="2" Grid.Row="0"
|
||||
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.RowSpan="2" Grid.Row="0"
|
||||
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.RowSpan="2" Grid.Row="0"
|
||||
Click="DeleteButton_Click"/>
|
||||
|
||||
<Grid Grid.Row="1" Grid.Column="1" Margin="0,0,10,10">
|
||||
<ScottPlot:WpfPlot x:Name="OechslePricePlot" IsEnabled="False"
|
||||
MouseWheel="OechslePricePlot_MouseWheel" MouseMove="OechslePricePlot_MouseMove" MouseDown="OechslePricePlot_MouseDown"/>
|
||||
</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>
|
||||
<RowDefinition Height="120"/>
|
||||
<RowDefinition Height="120"/>
|
||||
<RowDefinition Height="90"/>
|
||||
<RowDefinition Height="210"/>
|
||||
<RowDefinition Height="1*"/>
|
||||
<RowDefinition Height="110"/>
|
||||
<RowDefinition Height="42"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<GroupBox Header="Graph" 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">
|
||||
<GroupBox Header="Datenpunkt" Grid.Row="0" Margin="0,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="85"/>
|
||||
@@ -138,35 +125,44 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Content="Oechsle:" Margin="10,10,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="OechsleInput" Grid.Column="1" HorizontalAlignment="Left" Margin="0,10,0,0" Text="" Width="90" TextChanged="OechsleInput_TextChanged" LostFocus="OechsleInput_LostFocus"/>
|
||||
<ctrl:UnitTextBox x:Name="OechsleInput" Unit="°Oe" TextChanged="OechsleInput_TextChanged" IsEnabled="False" LostFocus="OechsleInput_LostFocus"
|
||||
Grid.Column="1" Width="90" Margin="0,10,0,0" HorizontalAlignment="Left" VerticalAlignment="Top"/>
|
||||
|
||||
<Label Content="Preis pro kg:" Margin="10,40,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="PriceInput" Grid.Column="1" HorizontalAlignment="Left" Margin="0,40,0,0" Text="" Width="90" TextChanged="PriceInput_TextChanged" LostFocus="PriceInput_LostFocus"/>
|
||||
|
||||
<Label Content="Preis:" Margin="10,40,0,0" Grid.Column="0"/>
|
||||
<ctrl:UnitTextBox x:Name="PriceInput" Unit="€/kg" TextChanged="PriceInput_TextChanged" IsEnabled="False" LostFocus="PriceInput_LostFocus"
|
||||
Grid.Column="1" Width="90" Margin="0,40,0,0" HorizontalAlignment="Left" VerticalAlignment="Top"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Gebunden Aufschlag" Grid.Row="1" Margin="0,5,5,5">
|
||||
<Grid>
|
||||
|
||||
<StackPanel Margin="10,10,0,0">
|
||||
<RadioButton x:Name="GebundenTypeFixed" GroupName="GebundenType" Checked="GebundenType_Checked" IsEnabled="False">Fix</RadioButton>
|
||||
<RadioButton x:Name="GebundenTypeGraph" GroupName="GebundenType" Checked="GebundenType_Checked" IsEnabled="False">Frei</RadioButton>
|
||||
<RadioButton x:Name="GebundenTypeNone" GroupName="GebundenType" Checked="GebundenType_Checked" IsEnabled="False">Nein</RadioButton>
|
||||
</StackPanel>
|
||||
|
||||
<ctrl:UnitTextBox x:Name="GebundenFlatBonus" Unit="€/kg" TextChanged="GebundenFlatBonus_TextChanged" IsEnabled="False"
|
||||
Width="90" Margin="5,5,5,5" HorizontalAlignment="Right" VerticalAlignment="Top" Grid.Column="1"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Aktionen" Grid.Row="2" Margin="0,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button x:Name="LeftFlatButton" Content="Links flach" Click="LeftFlatButton_Click" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10,10,10,10"/>
|
||||
|
||||
<Button x:Name="RightFlatButton" Content="Rechts flach" Click="RightFlatButton_Click" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10,50,10,10"/>
|
||||
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10,45,10,10"/>
|
||||
<Button x:Name="InterpolateButton" Content="Interpolieren" Click="InterpolateButton_Click" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10,90,10,10"/>
|
||||
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10,80,10,10"/>
|
||||
<Button x:Name="LinearIncreaseButton" Content="Linear wachsen" Click="LinearIncreaseButton_Click" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10,130,10,10"/>
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10,115,10,10"/>
|
||||
</Grid>
|
||||
</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.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
@@ -186,4 +182,4 @@
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</local:AdministrationWindow>
|
||||
</local:ContextWindow>
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -100,7 +100,8 @@
|
||||
<LineBreak/>
|
||||
Filtern nach:<LineBreak/>
|
||||
<Bold>Sorte</Bold>: z.B. GV, ZW, rr, sa, !gv (ausgenommen GV), ...<LineBreak/>
|
||||
<Bold>Qualitätsstufe</Bold>: z.B. QUW, kab, ldw, ...<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/>
|
||||
@@ -147,21 +148,21 @@
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Sorte" Binding="{Binding SortIdString}" Width="50">
|
||||
<DataGridTextColumn Header="Sorte" Binding="{Binding FilteredSortIdString}" Width="50">
|
||||
<DataGridTextColumn.CellStyle>
|
||||
<Style>
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Gewicht" Binding="{Binding Weight, StringFormat='{}{0:N0} kg '}" Width="75">
|
||||
<DataGridTextColumn Header="Gewicht" Binding="{Binding FilteredWeight, StringFormat='{}{0:N0} kg '}" Width="75">
|
||||
<DataGridTextColumn.CellStyle>
|
||||
<Style>
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Gradation" Binding="{Binding Kmw, StringFormat='{}{0:N1}° '}" Width="50">
|
||||
<DataGridTextColumn Header="Gradation" Binding="{Binding FilteredKmw, StringFormat='{}{0:N1}° '}" Width="50">
|
||||
<DataGridTextColumn.CellStyle>
|
||||
<Style>
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
|
||||
|
@@ -10,6 +10,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
@@ -27,7 +28,10 @@ namespace Elwig.Windows {
|
||||
private Member? Member = null;
|
||||
private readonly DispatcherTimer Timer;
|
||||
private List<string> TextFilter = [];
|
||||
private readonly RoutedCommand CtrlF = new();
|
||||
|
||||
private readonly RoutedCommand CtrlF = new("CtrlF", typeof(DeliveryAdminWindow), [new KeyGesture(Key.F, ModifierKeys.Control)]);
|
||||
private readonly RoutedCommand CtrlP = new("CtrlP", typeof(DeliveryAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control)]);
|
||||
private readonly RoutedCommand CtrlShiftP = new("CtrlShiftP", typeof(DeliveryAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control | ModifierKeys.Shift)]);
|
||||
|
||||
private string? LastScaleError = null;
|
||||
private string? ManualWeighingReason = null;
|
||||
@@ -37,8 +41,9 @@ namespace Elwig.Windows {
|
||||
|
||||
public DeliveryAdminWindow(bool receipt = false) {
|
||||
InitializeComponent();
|
||||
CtrlF.InputGestures.Add(new KeyGesture(Key.F, ModifierKeys.Control));
|
||||
CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
|
||||
CommandBindings.Add(new CommandBinding(CtrlP, Menu_Print_ShowDeliveryNote_Click));
|
||||
CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_Print_PrintDeliveryNote_Click));
|
||||
RequiredInputs = [
|
||||
MgNrInput, MemberInput,
|
||||
LsNrInput, DateInput, BranchInput,
|
||||
@@ -168,7 +173,7 @@ namespace Elwig.Windows {
|
||||
|
||||
private async void Menu_Print_DeliveryJournal_ShowFilter_Click(object sender, RoutedEventArgs evt) {
|
||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
||||
var (f, _, d, _) = await GetFilters();
|
||||
var (f, _, d, _, _) = await GetFilters();
|
||||
var doc = new DeliveryJournal(string.Join(" / ", f), d);
|
||||
await doc.Generate();
|
||||
Mouse.OverrideCursor = null;
|
||||
@@ -177,7 +182,7 @@ namespace Elwig.Windows {
|
||||
|
||||
private async void Menu_Print_DeliveryJournal_PrintFilter_Click(object sender, RoutedEventArgs evt) {
|
||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
||||
var (f, _, d, _) = await GetFilters();
|
||||
var (f, _, d, _, _) = await GetFilters();
|
||||
var doc = new DeliveryJournal(string.Join(" / ", f), d);
|
||||
await doc.Generate();
|
||||
Mouse.OverrideCursor = null;
|
||||
@@ -296,7 +301,7 @@ namespace Elwig.Windows {
|
||||
await RefreshDeliveryListQuery();
|
||||
}
|
||||
|
||||
private async Task<(List<string>, IQueryable<Delivery>, IQueryable<DeliveryPart>, List<string>)> GetFilters() {
|
||||
private async Task<(List<string>, IQueryable<Delivery>, IQueryable<DeliveryPart>, Predicate<DeliveryPart>, List<string>)> GetFilters() {
|
||||
List<string> filterNames = [];
|
||||
IQueryable<Delivery> deliveryQuery = Context.Deliveries;
|
||||
if (IsReceipt && App.BranchNum > 1) {
|
||||
@@ -316,16 +321,13 @@ namespace Elwig.Windows {
|
||||
deliveryQuery = deliveryQuery.Where(d => d.Year == SeasonInput.Value);
|
||||
filterNames.Add(SeasonInput.Value.ToString() ?? "");
|
||||
}
|
||||
IQueryable<DeliveryPart> dpq = deliveryQuery
|
||||
.SelectMany(d => d.Parts)
|
||||
.OrderBy(p => p.Delivery.DateString)
|
||||
.ThenBy(p => p.Delivery.TimeString)
|
||||
.ThenBy(p => p.Delivery.LsNr)
|
||||
.ThenBy(p => p.DPNr);
|
||||
|
||||
Expression<Func<DeliveryPart, bool>> prd = p => true;
|
||||
|
||||
var filterVar = new List<string>();
|
||||
var filterNotVar = new List<string>();
|
||||
var filterQual = new List<string>();
|
||||
var filterNotQual = new List<string>();
|
||||
var filterMgNr = new List<int>();
|
||||
var filterZwst = new List<string>();
|
||||
var filterAttr = new List<string>();
|
||||
@@ -346,7 +348,15 @@ namespace Elwig.Windows {
|
||||
|
||||
for (int i = 0; i < filter.Count; 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());
|
||||
filter.RemoveAt(i--);
|
||||
filterNames.Add(var[e.ToUpper()].Name);
|
||||
@@ -358,6 +368,10 @@ namespace Elwig.Windows {
|
||||
filterQual.Add(e.ToUpper());
|
||||
filter.RemoveAt(i--);
|
||||
filterNames.Add(qual[e.ToUpper()].Name);
|
||||
} 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));
|
||||
filter.RemoveAt(i--);
|
||||
@@ -474,31 +488,32 @@ namespace Elwig.Windows {
|
||||
}
|
||||
}
|
||||
|
||||
if (filterYearGt > 0) dpq = dpq.Where(p => p.Year >= filterYearGt);
|
||||
if (filterYearLt > 0) dpq = dpq.Where(p => p.Year < filterYearLt);
|
||||
if (filterMgNr.Count > 0) dpq = dpq.Where(p => filterMgNr.Contains(p.Delivery.MgNr));
|
||||
if (filterYearGt > 0) prd = prd.And(p => p.Year >= filterYearGt);
|
||||
if (filterYearLt > 0) prd = prd.And(p => p.Year < filterYearLt);
|
||||
if (filterMgNr.Count > 0) prd = prd.And(p => filterMgNr.Contains(p.Delivery.MgNr));
|
||||
if (filterDate.Count > 0) {
|
||||
var pr = PredicateBuilder.New<DeliveryPart>(false);
|
||||
foreach (var (d1, d2) in filterDate)
|
||||
pr.Or(p => (d1 == null || d1.CompareTo(p.Delivery.DateString.Substring(10 - d1.Length)) <= 0) && (d2 == null || d2.CompareTo(p.Delivery.DateString.Substring(10 - d2.Length)) >= 0));
|
||||
dpq = dpq.Where(pr);
|
||||
prd = prd.And(pr);
|
||||
}
|
||||
if (filterTime.Count > 0) {
|
||||
var pr = PredicateBuilder.New<DeliveryPart>(false);
|
||||
foreach (var (t1, t2) in filterTime)
|
||||
pr.Or(p => (t1 == null || t1.CompareTo(p.Delivery.TimeString) <= 0) && (t2 == null || t2.CompareTo(p.Delivery.TimeString) > 0));
|
||||
dpq = dpq.Where(p => p.Delivery.TimeString != null).Where(pr);
|
||||
prd = prd.And(p => p.Delivery.TimeString != null).And(pr);
|
||||
}
|
||||
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 (filterQual.Count > 0) dpq = dpq.Where(p => filterQual.Contains(p.QualId));
|
||||
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 (filterNotAttr.Count > 0) dpq = dpq.Where(p => p.AttrId == null || !filterAttr.Contains(p.AttrId));
|
||||
if (filterKmwGt > 0) dpq = dpq.Where(p => p.Kmw >= filterKmwGt);
|
||||
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 (filterOeLt > 0) dpq = dpq.Where(p => p.Kmw * (4.54 + 0.022 * p.Kmw) < filterOeLt);
|
||||
if (filterVar.Count > 0) prd = prd.And(p => filterVar.Contains(p.SortId));
|
||||
if (filterNotVar.Count > 0) prd = prd.And(p => !filterNotVar.Contains(p.SortId));
|
||||
if (filterQual.Count > 0) prd = prd.And(p => filterQual.Contains(p.QualId));
|
||||
if (filterNotQual.Count > 0) prd = prd.And(p => !filterNotQual.Contains(p.QualId));
|
||||
if (filterZwst.Count > 0) prd = prd.And(p => filterZwst.Contains(p.Delivery.ZwstId));
|
||||
if (filterAttr.Count > 0) prd = prd.And(p => p.AttrId != null && filterAttr.Contains(p.AttrId));
|
||||
if (filterNotAttr.Count > 0) prd = prd.And(p => p.AttrId == null || !filterNotAttr.Contains(p.AttrId));
|
||||
if (filterKmwGt > 0) prd = prd.And(p => p.Kmw >= filterKmwGt);
|
||||
if (filterKmwLt > 0) prd = prd.And(p => p.Kmw < filterKmwLt);
|
||||
if (filterOeGt > 0) prd = prd.And(p => p.Kmw * (4.54 + 0.022 * p.Kmw) >= filterOeGt);
|
||||
if (filterOeLt > 0) prd = prd.And(p => p.Kmw * (4.54 + 0.022 * p.Kmw) < filterOeLt);
|
||||
|
||||
if (filterYearGt > 0 && filterYearLt > 0) {
|
||||
filterNames.Insert(0, $"{filterYearGt}–{filterYearLt - 1}");
|
||||
@@ -523,7 +538,15 @@ namespace Elwig.Windows {
|
||||
}
|
||||
}
|
||||
|
||||
return (filterNames, dpq.Select(p => p.Delivery).Distinct().OrderBy(d => d.DateString).ThenBy(d => d.TimeString), dpq, filter);
|
||||
IQueryable<DeliveryPart> dpq = deliveryQuery
|
||||
.SelectMany(d => d.Parts)
|
||||
.Where(prd)
|
||||
.OrderBy(p => p.Delivery.DateString)
|
||||
.ThenBy(p => p.Delivery.TimeString)
|
||||
.ThenBy(p => p.Delivery.LsNr)
|
||||
.ThenBy(p => p.DPNr);
|
||||
|
||||
return (filterNames, dpq.Select(p => p.Delivery).Distinct().OrderBy(d => d.DateString).ThenBy(d => d.TimeString), dpq, prd.Invoke, filter);
|
||||
}
|
||||
|
||||
private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
|
||||
@@ -559,7 +582,7 @@ namespace Elwig.Windows {
|
||||
}
|
||||
|
||||
private async Task RefreshDeliveryListQuery(bool updateSort = false) {
|
||||
var (_, deliveryQuery, deliveryPartsQuery, filter) = await GetFilters();
|
||||
var (_, deliveryQuery, deliveryPartsQuery, predicate, filter) = await GetFilters();
|
||||
var deliveries = await deliveryQuery.ToListAsync();
|
||||
deliveries.Reverse();
|
||||
|
||||
@@ -575,8 +598,10 @@ namespace Elwig.Windows {
|
||||
.ToList();
|
||||
}
|
||||
|
||||
deliveries.ForEach(d => { d.PartFilter = predicate; });
|
||||
ControlUtils.RenewItemsSource(DeliveryList, deliveries, d => ((d as Delivery)?.Year, (d as Delivery)?.DId),
|
||||
DeliveryList_SelectionChanged, filter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
|
||||
await RefreshDeliveryParts();
|
||||
|
||||
var members = deliveries.Select(d => d.Member).DistinctBy(m => m.MgNr).ToList();
|
||||
StatusMembers.Text = $"Mitglieder: {members.Count}" + (members.Count > 0 && members.Count <= 4 ? $" ({string.Join(", ", members.Select(m => m.AdministrativeName))})" : "");
|
||||
@@ -708,7 +733,7 @@ namespace Elwig.Windows {
|
||||
Menu_Export_Bki.Items.Clear();
|
||||
foreach (var s in await Context.Seasons.OrderByDescending(s => s.Year).ToListAsync()) {
|
||||
var i = new MenuItem {
|
||||
Header = $"Season {s.Year}",
|
||||
Header = $"Saison {s.Year}",
|
||||
};
|
||||
i.Click += Menu_Export_Bki_Click;
|
||||
Menu_Export_Bki.Items.Add(i);
|
||||
@@ -746,7 +771,7 @@ namespace Elwig.Windows {
|
||||
private async Task RefreshDeliveryParts() {
|
||||
if (DeliveryList.SelectedItem is Delivery d) {
|
||||
ControlUtils.RenewItemsSource(ModifiersInput, await Context.Modifiers.Where(m => m.Year == d.Year).OrderBy(m => m.Ordering).ToListAsync(), i => (i as Modifier)?.ModId);
|
||||
ControlUtils.RenewItemsSource(DeliveryPartList, d.Parts.OrderBy(p => p.DPNr).ToList(), i => ((i as DeliveryPart)?.Year, (i as DeliveryPart)?.DId, (i as DeliveryPart)?.DPNr), DeliveryPartList_SelectionChanged, ControlUtils.RenewSourceDefault.First);
|
||||
ControlUtils.RenewItemsSource(DeliveryPartList, d.FilteredParts.OrderBy(p => p.DPNr).ToList(), i => ((i as DeliveryPart)?.Year, (i as DeliveryPart)?.DId, (i as DeliveryPart)?.DPNr), DeliveryPartList_SelectionChanged, ControlUtils.RenewSourceDefault.First);
|
||||
} else {
|
||||
ControlUtils.RenewItemsSource(ModifiersInput, await Context.Modifiers.Where(m => m.Year == Utils.CurrentLastSeason).OrderBy(m => m.Ordering).ToListAsync(), i => (i as Modifier)?.ModId);
|
||||
DeliveryPartList.ItemsSource = null;
|
||||
@@ -875,7 +900,7 @@ namespace Elwig.Windows {
|
||||
p.Acid = (AcidInput.Text == "") ? null : double.Parse(AcidInput.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.ScaleId = ScaleId;
|
||||
p.WeighingId = WeighingId;
|
||||
@@ -1120,7 +1145,7 @@ namespace Elwig.Windows {
|
||||
} else {
|
||||
// switch to last delivery part
|
||||
DeliveryPartList.IsEnabled = true;
|
||||
DeliveryPartList.SelectedItem = d.Parts.Last();
|
||||
DeliveryPartList.SelectedItem = d.FilteredParts.Last();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -76,7 +76,7 @@ namespace Elwig.Dialogs {
|
||||
IEnumerable<Member> list = await members.ToListAsync();
|
||||
var data = await DeliveryConfirmationData.ForSeason(Context.DeliveryParts, Year);
|
||||
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
|
||||
}
|
||||
));
|
||||
|
@@ -17,15 +17,19 @@ namespace Elwig.Windows {
|
||||
public partial class MemberAdminWindow : AdministrationWindow {
|
||||
|
||||
private List<string> TextFilter = [];
|
||||
private readonly RoutedCommand CtrlF = new();
|
||||
private readonly (ComboBox, TextBox, TextBox)[] PhoneNrInputs;
|
||||
|
||||
private readonly RoutedCommand CtrlF = new("CtrlF", typeof(MemberAdminWindow), [new KeyGesture(Key.F, ModifierKeys.Control)]);
|
||||
private readonly RoutedCommand CtrlP = new("CtrlP", typeof(MemberAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control)]);
|
||||
private readonly RoutedCommand CtrlShiftP = new("CtrlShiftP", typeof(MemberAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control | ModifierKeys.Shift)]);
|
||||
|
||||
private static ObservableCollection<KeyValuePair<string, string>> PhoneNrTypes { get; set; } = new(Utils.PhoneNrTypes);
|
||||
|
||||
public MemberAdminWindow() {
|
||||
InitializeComponent();
|
||||
CtrlF.InputGestures.Add(new KeyGesture(Key.F, ModifierKeys.Control));
|
||||
CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
|
||||
CommandBindings.Add(new CommandBinding(CtrlP, Menu_Show_MemberDataSheet_Click));
|
||||
CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_Print_MemberDataSheet_Click));
|
||||
ExemptInputs = [
|
||||
SearchInput, ActiveMemberInput, MemberList,
|
||||
];
|
||||
|
@@ -52,6 +52,8 @@ namespace Elwig.Windows {
|
||||
Arrow3.Content = locked ? "\xF0B0" : "\xF0AF";
|
||||
CopyButton.IsEnabled = true;
|
||||
EditButton.Content = locked ? "Ansehen" : "Bearbeiten";
|
||||
EditButton.IsEnabled = true;
|
||||
SaveButton.IsEnabled = !locked;
|
||||
ShowButton.IsEnabled = true;
|
||||
PrintButton.IsEnabled = true;
|
||||
ExportButton.IsEnabled = locked;
|
||||
@@ -67,30 +69,27 @@ namespace Elwig.Windows {
|
||||
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;
|
||||
}
|
||||
ConsiderModifiersInput.IsEnabled = !locked;
|
||||
ConsiderPenaltiesInput.IsEnabled = !locked;
|
||||
ConsiderPenaltyInput.IsEnabled = !locked;
|
||||
ConsiderAutoInput.IsEnabled = !locked;
|
||||
DataInput.IsReadOnly = locked;
|
||||
} else {
|
||||
EditButton.Content = "Bearbeiten";
|
||||
EditButton.IsEnabled = false;
|
||||
SaveButton.IsEnabled = false;
|
||||
CopyButton.IsEnabled = false;
|
||||
CalculateButton.IsEnabled = false;
|
||||
CommitButton.IsEnabled = false;
|
||||
@@ -130,11 +129,12 @@ namespace Elwig.Windows {
|
||||
private void UpdateSaveButton() {
|
||||
SaveButton.IsEnabled = PaymentVariantList.SelectedItem != null &&
|
||||
((DataChanged && DataValid) || NameChanged || CommentChanged ||
|
||||
(TransferDateChanged && TransferDateValid)) ||
|
||||
(TransferDateChanged && TransferDateValid) ||
|
||||
(ConsiderModifiersInput.IsChecked != BillingData?.ConsiderDelieryModifiers) ||
|
||||
(ConsiderPenaltiesInput.IsChecked != BillingData?.ConsiderContractPenalties) ||
|
||||
(ConsiderPenaltyInput.IsChecked != BillingData?.ConsiderTotalPenalty) ||
|
||||
(ConsiderAutoInput.IsChecked != BillingData?.ConsiderAutoBusinessShares);
|
||||
(ConsiderAutoInput.IsChecked != BillingData?.ConsiderAutoBusinessShares));
|
||||
CalculateButton.IsEnabled = !SaveButton.IsEnabled && PaymentVariantList.SelectedItem is PaymentVar { TestVariant: true };
|
||||
}
|
||||
|
||||
private void UpdateSums() {
|
||||
@@ -173,7 +173,7 @@ namespace Elwig.Windows {
|
||||
v.Name = "Neue Auszahlungsvariante";
|
||||
v.TestVariant = true;
|
||||
v.DateString = $"{DateTime.Today:yyyy-MM-dd}";
|
||||
v.Data = "{\"mode\": \"elwig\", \"version\": 1, \"payment\": 1.0, \"curves\": []}";
|
||||
v.Data = "{\"mode\": \"elwig\", \"version\": 1, \"payment\": 1.0, \"quality\": {\"WEI\": 0}, \"curves\": []}";
|
||||
|
||||
await Context.AddAsync(v);
|
||||
await Context.SaveChangesAsync();
|
||||
@@ -259,8 +259,12 @@ namespace Elwig.Windows {
|
||||
return;
|
||||
CommitButton.IsEnabled = false;
|
||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
||||
var b = new BillingVariant(v.Year, v.AvNr);
|
||||
await b.Commit();
|
||||
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();
|
||||
@@ -396,13 +400,13 @@ namespace Elwig.Windows {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var json = BillingData.ParseJson(DataInput.Text);
|
||||
var data = BillingData.FromJson(DataInput.Text);
|
||||
var origJson = v.Data;
|
||||
try {
|
||||
origJson = JsonSerializer.Serialize(BillingData.ParseJson(v.Data));
|
||||
origJson = JsonSerializer.Serialize(BillingData.FromJson(v.Data).Data);
|
||||
} catch { }
|
||||
DataValid = true;
|
||||
if (JsonSerializer.Serialize(json) != origJson) {
|
||||
if (JsonSerializer.Serialize(data.Data) != origJson) {
|
||||
ControlUtils.SetInputChanged(DataInput);
|
||||
DataChanged = true;
|
||||
} else {
|
||||
|
@@ -86,7 +86,7 @@ namespace Elwig.Windows {
|
||||
private async void AutoBusinessSharesButton_Click(object sender, RoutedEventArgs evt) {
|
||||
if (SeasonInput.Value is not int year)
|
||||
return;
|
||||
if (App.Client.IsMatzen) {
|
||||
if (false && App.Client.IsMatzen) {
|
||||
AutoBusinessSharesButton.IsEnabled = false;
|
||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
||||
|
||||
|
@@ -60,6 +60,6 @@
|
||||
<None Include="Files\config.ini" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="WixToolset.Heat" Version="4.0.1" />
|
||||
<PackageReference Include="WixToolset.Heat" Version="4.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@@ -13,7 +13,7 @@
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Installer\Installer.wixproj" />
|
||||
<PackageReference Include="WixToolset.Bal.wixext" Version="4.0.1" />
|
||||
<PackageReference Include="WixToolset.Util.wixext" Version="4.0.1" />
|
||||
<PackageReference Include="WixToolset.Bal.wixext" Version="4.0.3" />
|
||||
<PackageReference Include="WixToolset.Util.wixext" Version="4.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@@ -1,10 +1,13 @@
|
||||
using Elwig.Helpers;
|
||||
using Elwig.Helpers.Billing;
|
||||
using Elwig.Models.Entities;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Tests.Helpers {
|
||||
namespace Tests.HelperTests {
|
||||
[TestFixture]
|
||||
public class BillingDataTest {
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOpts = new() { WriteIndented = true };
|
||||
private static readonly string[] AttributeVariants = ["GV", "GVD", "GVK", "GVS", "GVZ", "WR", "WRS", "ZW", "ZWS", "ZWZ"];
|
||||
|
||||
[OneTimeSetUp]
|
||||
@@ -26,14 +29,14 @@ namespace Tests.Helpers {
|
||||
};
|
||||
}
|
||||
|
||||
private static void TestCalcOe(BillingData data, string bucket, double oe, decimal expected, string? qualid = null, bool geb = false) {
|
||||
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(BillingData data, string bucket, double kmw, decimal expected, string? qualid = null, bool geb = false) {
|
||||
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);
|
||||
@@ -41,8 +44,8 @@ namespace Tests.Helpers {
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_01_Flatrate() {
|
||||
var data = BillingData.FromJson("""
|
||||
public void TestRead_01_Flatrate() {
|
||||
var data = PaymentBillingData.FromJson("""
|
||||
{
|
||||
"mode": "elwig",
|
||||
"version": 1,
|
||||
@@ -57,8 +60,8 @@ namespace Tests.Helpers {
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_02_Simple() {
|
||||
var data = BillingData.FromJson("""
|
||||
public void TestRead_02_Simple() {
|
||||
var data = PaymentBillingData.FromJson("""
|
||||
{
|
||||
"mode": "elwig",
|
||||
"version": 1,
|
||||
@@ -92,8 +95,8 @@ namespace Tests.Helpers {
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_03_GreaterThanAndLessThan() {
|
||||
var data = BillingData.FromJson("""
|
||||
public void TestRead_03_GreaterThanAndLessThan() {
|
||||
var data = PaymentBillingData.FromJson("""
|
||||
{
|
||||
"mode": "elwig",
|
||||
"version": 1,
|
||||
@@ -131,8 +134,8 @@ namespace Tests.Helpers {
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_04_VariantsAndAttributes() {
|
||||
var data = BillingData.FromJson("""
|
||||
public void TestRead_04_VariantsAndAttributes() {
|
||||
var data = PaymentBillingData.FromJson("""
|
||||
{
|
||||
"mode": "elwig",
|
||||
"version": 1,
|
||||
@@ -161,8 +164,8 @@ namespace Tests.Helpers {
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_05_QualityLevel() {
|
||||
var data = BillingData.FromJson("""
|
||||
public void TestRead_05_QualityLevel() {
|
||||
var data = PaymentBillingData.FromJson("""
|
||||
{
|
||||
"mode": "elwig",
|
||||
"version": 1,
|
||||
@@ -192,8 +195,8 @@ namespace Tests.Helpers {
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_06_ModeOeAndKmw() {
|
||||
var data = BillingData.FromJson("""
|
||||
public void TestRead_06_ModeOeAndKmw() {
|
||||
var data = PaymentBillingData.FromJson("""
|
||||
{
|
||||
"mode": "elwig",
|
||||
"version": 1,
|
||||
@@ -234,8 +237,8 @@ namespace Tests.Helpers {
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_07_MultipleCurves() {
|
||||
var data = BillingData.FromJson("""
|
||||
public void TestRead_07_MultipleCurves() {
|
||||
var data = PaymentBillingData.FromJson("""
|
||||
{
|
||||
"mode": "elwig",
|
||||
"version": 1,
|
||||
@@ -303,8 +306,8 @@ namespace Tests.Helpers {
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_08_WgMaster() {
|
||||
var data = BillingData.FromJson("""
|
||||
public void TestRead_08_WgMaster() {
|
||||
var data = PaymentBillingData.FromJson("""
|
||||
{
|
||||
"mode": "wgmaster",
|
||||
"Grundbetrag": 0.033,
|
||||
@@ -345,5 +348,162 @@ namespace Tests.Helpers {
|
||||
TestCalcOe(data, "GVK", 115, 0.065m);
|
||||
});
|
||||
}
|
||||
|
||||
private static List<ContractSelection> GetSelection(IEnumerable<string> attVars) {
|
||||
return attVars.Select(s => {
|
||||
var sortid = s[..2];
|
||||
var attrid = s.Length > 2 ? s[2..] : null;
|
||||
return new ContractSelection(
|
||||
new WineVar(sortid, sortid),
|
||||
attrid == null ? null : new WineAttr(attrid, attrid)
|
||||
);
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWrite_01_Empty() {
|
||||
List<GraphEntry> entries = [
|
||||
new GraphEntry(1, 4, BillingData.CurveMode.Oe, new() {
|
||||
[73] = 0.5m
|
||||
}, null)
|
||||
];
|
||||
var updated = BillingData.FromGraphEntries(entries);
|
||||
Assert.That(updated.ToJsonString(JsonOpts), Is.EqualTo("""
|
||||
{
|
||||
"mode": "elwig",
|
||||
"version": 1,
|
||||
"payment": 0,
|
||||
"curves": []
|
||||
}
|
||||
"""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWrite_02_Flatrate() {
|
||||
List<GraphEntry> entries = [
|
||||
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
|
||||
[73] = 0.5m
|
||||
}, null), GetSelection(["GV"]))
|
||||
];
|
||||
var data = BillingData.FromGraphEntries(entries);
|
||||
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
|
||||
{
|
||||
"mode": "elwig",
|
||||
"version": 1,
|
||||
"payment": 0.5,
|
||||
"curves": []
|
||||
}
|
||||
"""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWrite_03_SingleCurve() {
|
||||
List<GraphEntry> entries = [
|
||||
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
|
||||
[73] = 0.5m,
|
||||
[83] = 1.0m
|
||||
}, null), GetSelection(["GV"]))
|
||||
];
|
||||
var data = BillingData.FromGraphEntries(entries);
|
||||
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
|
||||
{
|
||||
"mode": "elwig",
|
||||
"version": 1,
|
||||
"payment": "curve:1",
|
||||
"curves": [
|
||||
{
|
||||
"id": 1,
|
||||
"mode": "oe",
|
||||
"data": {
|
||||
"73oe": 0.5,
|
||||
"83oe": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWrite_04_Simple() {
|
||||
List<GraphEntry> entries = [
|
||||
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
|
||||
[73] = 0.5m,
|
||||
[84] = 1.0m
|
||||
}, null), GetSelection(["GV", "ZW"])),
|
||||
new GraphEntry(10, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
|
||||
[73] = 0.75m,
|
||||
}, null), GetSelection(["WR"]))
|
||||
];
|
||||
var data = BillingData.FromGraphEntries(entries);
|
||||
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
|
||||
{
|
||||
"mode": "elwig",
|
||||
"version": 1,
|
||||
"payment": {
|
||||
"WR/": 0.75,
|
||||
"default": "curve:1"
|
||||
},
|
||||
"curves": [
|
||||
{
|
||||
"id": 1,
|
||||
"mode": "oe",
|
||||
"data": {
|
||||
"73oe": 0.5,
|
||||
"84oe": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWrite_05_Attribute() {
|
||||
List<GraphEntry> entries = [
|
||||
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
|
||||
[73] = 0.5m,
|
||||
[84] = 1.0m
|
||||
}, null), GetSelection(["GVB", "ZWB"])),
|
||||
new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
|
||||
[73] = 0.75m,
|
||||
}, null), GetSelection(["WR", "BL", "RR", "FV"])),
|
||||
new GraphEntry(4, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
|
||||
[73] = 0.65m,
|
||||
[84] = 1.2m
|
||||
}, null), GetSelection(["BP", "SA"]))
|
||||
];
|
||||
var data = BillingData.FromGraphEntries(entries);
|
||||
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
|
||||
{
|
||||
"mode": "elwig",
|
||||
"version": 1,
|
||||
"payment": {
|
||||
"BP/": "curve:2",
|
||||
"SA/": "curve:2",
|
||||
"default": 0.75,
|
||||
"/B": "curve:1"
|
||||
},
|
||||
"curves": [
|
||||
{
|
||||
"id": 1,
|
||||
"mode": "oe",
|
||||
"data": {
|
||||
"73oe": 0.5,
|
||||
"84oe": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"mode": "oe",
|
||||
"data": {
|
||||
"73oe": 0.65,
|
||||
"84oe": 1.2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"""));
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
using Microsoft.Data.Sqlite;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Tests.Helpers {
|
||||
namespace Tests.HelperTests {
|
||||
[TestFixture]
|
||||
public class BillingTest {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
using Elwig.Helpers;
|
||||
|
||||
namespace Tests.Helpers {
|
||||
namespace Tests.HelperTests {
|
||||
[TestFixture]
|
||||
public class UtilsTest {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
using Elwig.Helpers;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Tests.Helpers {
|
||||
namespace Tests.HelperTests {
|
||||
[TestFixture]
|
||||
[Apartment(ApartmentState.STA)]
|
||||
public class ValidatorTest {
|
@@ -13,16 +13,22 @@
|
||||
<EmbeddedResource Include="Resources\*.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
<Target Name="FetchResources" BeforeTargets="BeforeBuild">
|
||||
<Exec Command="call fetch-resources.bat" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.5.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2" />
|
||||
<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>
|
||||
|
Reference in New Issue
Block a user