Billing: Split BillingData into BillingData and PaymentBillingData

This commit is contained in:
2024-01-17 22:49:53 +01:00
parent 4dd036babd
commit f886888ccc
5 changed files with 149 additions and 124 deletions

View File

@ -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,15 +56,10 @@ 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) {
@ -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,9 +117,9 @@ namespace Elwig.Helpers.Billing {
return dict;
}
public static Dictionary<int, Curve> GetCurves(JsonObject data, CalculationMode mode) {
public 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();
@ -147,108 +145,5 @@ namespace Elwig.Helpers.Billing {
}
return dict;
}
private Dictionary<string, Curve> GetData(JsonObject data, IEnumerable<string> attributeVariants) {
Dictionary<string, Curve> dict;
if (data["default"] is JsonValue def) {
var c = LookupCurve(def);
dict = attributeVariants.ToDictionary(e => e, _ => c);
} else {
dict = [];
}
var variants = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length == 2);
var attributes = data.Where(p => p.Key.StartsWith('/'));
var others = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length > 2);
foreach (var (idx, v) in variants) {
var curve = LookupCurve(v?.AsValue() ?? throw new InvalidOperationException());
foreach (var i in attributeVariants.Where(e => e.StartsWith(idx[..^1]))) {
dict[i] = curve;
}
}
foreach (var (idx, v) in attributes) {
var curve = LookupCurve(v?.AsValue() ?? throw new InvalidOperationException());
foreach (var i in attributeVariants.Where(e => e[2..] == idx[1..])) {
dict[i] = curve;
}
}
foreach (var (idx, v) in others) {
var curve = LookupCurve(v?.AsValue() ?? throw new InvalidOperationException());
dict[idx.Replace("/", "")] = curve;
}
return dict;
}
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 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);
if (!lt.Any()) {
return d[gt.Min()];
} else if (!gt.Any()) {
return d[lt.Max()];
}
var max = lt.Max();
var min = gt.Min();
if (max == min) return d[r];
var p1 = ((decimal)r - (decimal)min) / ((decimal)max - (decimal)min);
var p2 = 1 - p1;
return d[min] * p2 + d[max] * p1;
}
private Curve LookupCurve(JsonValue val) {
if (val.TryGetValue(out string? curve)) {
var curveId = int.Parse(curve.Split(":")[1]);
return Curves[curveId];
} else if (val.TryGetValue(out decimal value)) {
return new(CurveMode.Oe, new() { { 73, value } }, null);
}
throw new InvalidOperationException();
}
public Curve GetCurve(string sortid, string? attrid) {
return PaymentData[$"{sortid}{attrid ?? ""}"];
}
public Curve? GetQualityCurve(string qualid, string sortid, string? attrid) {
return QualityData.TryGetValue($"{qualid}/{sortid}{attrid ?? ""}", out var curve) ? curve : null;
}
}
}

View File

@ -10,7 +10,7 @@ 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;
@ -22,7 +22,7 @@ namespace Elwig.Helpers.Billing {
.ToList()
.Union(Context.WineVarieties.Select(v => v.SortId))
.ToList();
Data = BillingData.FromJson(PaymentVariant.Data, attrVariants);
Data = PaymentBillingData.FromJson(PaymentVariant.Data, attrVariants);
}
public async Task Calculate() {

View File

@ -3,7 +3,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Nodes;
using System.Windows.Documents;
namespace Elwig.Helpers.Billing {
public class Graph : ICloneable {

View File

@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json.Nodes;
namespace Elwig.Helpers.Billing {
public class PaymentBillingData : BillingData {
protected readonly Dictionary<int, Curve> Curves;
protected readonly Dictionary<string, Curve> PaymentData;
protected readonly Dictionary<string, Curve> QualityData;
protected readonly IEnumerable<string> AttributeVariants;
public PaymentBillingData(JsonObject data, IEnumerable<string> attributeVariants) :
base(data) {
if (attributeVariants.Any(e => e.Any(c => c < 'A' || c > 'Z')))
throw new ArgumentException("Invalid attributeVariants");
AttributeVariants = attributeVariants;
Curves = GetCurves();
PaymentData = GetPaymentData();
QualityData = GetQualityData();
}
public static PaymentBillingData FromJson(string json, IEnumerable<string> attributeVariants) {
return new(ParseJson(json), attributeVariants);
}
private Dictionary<string, Curve> GetData(JsonObject data) {
Dictionary<string, Curve> dict;
if (data["default"] is JsonValue def) {
var c = LookupCurve(def);
dict = AttributeVariants.ToDictionary(e => e, _ => c);
} else {
dict = [];
}
var variants = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length == 2);
var attributes = data.Where(p => p.Key.StartsWith('/'));
var others = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length > 2);
foreach (var (idx, v) in variants) {
var curve = LookupCurve(v?.AsValue() ?? throw new InvalidOperationException());
foreach (var i in AttributeVariants.Where(e => e.StartsWith(idx[..^1]))) {
dict[i] = curve;
}
}
foreach (var (idx, v) in attributes) {
var curve = LookupCurve(v?.AsValue() ?? throw new InvalidOperationException());
foreach (var i in AttributeVariants.Where(e => e[2..] == idx[1..])) {
dict[i] = curve;
}
}
foreach (var (idx, v) in others) {
var curve = LookupCurve(v?.AsValue() ?? throw new InvalidOperationException());
dict[idx.Replace("/", "")] = curve;
}
return dict;
}
protected Dictionary<string, Curve> GetPaymentData() {
var p = GetPaymentEntry();
if (p is JsonValue val) {
var c = LookupCurve(val);
return AttributeVariants.ToDictionary(e => e, _ => c);
}
return GetData(p?.AsObject() ?? throw new InvalidOperationException());
}
protected Dictionary<string, Curve> GetQualityData() {
Dictionary<string, Curve> dict = [];
var q = GetQualityEntry();
foreach (var (qualid, data) in q) {
Dictionary<string, Curve> qualDict;
if (data is JsonValue val) {
var c = LookupCurve(val);
qualDict = AttributeVariants.ToDictionary(e => e, _ => c);
} else {
qualDict = GetData(data?.AsObject() ?? throw new InvalidOperationException());
}
foreach (var (idx, d) in qualDict) {
dict[$"{qualid}/{idx}"] = d;
}
}
return dict;
}
public decimal CalculatePrice(string sortid, string? attrid, string qualid, bool gebunden, double oe, double kmw) {
var curve = GetQualityCurve(qualid, sortid, attrid) ?? GetCurve(sortid, attrid);
var d = (gebunden ? curve.Gebunden : null) ?? curve.Normal;
if (d.Count == 1) return d.First().Value;
var r = curve.Mode == CurveMode.Oe ? oe : kmw;
var lt = d.Keys.Where(v => v <= r);
var gt = d.Keys.Where(v => v >= r);
if (!lt.Any()) {
return d[gt.Min()];
} else if (!gt.Any()) {
return d[lt.Max()];
}
var max = lt.Max();
var min = gt.Min();
if (max == min) return d[r];
var p1 = ((decimal)r - (decimal)min) / ((decimal)max - (decimal)min);
var p2 = 1 - p1;
return d[min] * p2 + d[max] * p1;
}
private Curve LookupCurve(JsonValue val) {
if (val.TryGetValue(out string? curve)) {
var curveId = int.Parse(curve.Split(":")[1]);
return Curves[curveId];
} else if (val.TryGetValue(out decimal value)) {
return new(CurveMode.Oe, new() { { 73, value } }, null);
}
throw new InvalidOperationException();
}
protected Curve GetCurve(string sortid, string? attrid) {
return PaymentData[$"{sortid}{attrid ?? ""}"];
}
protected Curve? GetQualityCurve(string qualid, string sortid, string? attrid) {
return QualityData.TryGetValue($"{qualid}/{sortid}{attrid ?? ""}", out var curve) ? curve : null;
}
}
}

View File

@ -71,7 +71,7 @@ namespace Elwig.Windows {
var data = ParseData(PaymentVar);
if (data == null) return;
var curves = BillingData.GetCurves(data, BillingData.CalculationMode.Elwig);
var curves = PaymentBillingData.GetCurves(data, BillingData.CalculationMode.Elwig);
foreach (var (id, curve) in curves) {
GraphEntries.Add(new GraphEntry(id, curve.Mode, curve.Normal, MinOechsle, MaxOechsle));