diff --git a/Elwig/Helpers/Billing/BillingData.cs b/Elwig/Helpers/Billing/BillingData.cs
index cb9259b..f32f371 100644
--- a/Elwig/Helpers/Billing/BillingData.cs
+++ b/Elwig/Helpers/Billing/BillingData.cs
@@ -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;
-        }
     }
 }
diff --git a/Elwig/Helpers/Billing/BillingVariant.cs b/Elwig/Helpers/Billing/BillingVariant.cs
index 028cb38..12216c6 100644
--- a/Elwig/Helpers/Billing/BillingVariant.cs
+++ b/Elwig/Helpers/Billing/BillingVariant.cs
@@ -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() {
diff --git a/Elwig/Helpers/Billing/Graph.cs b/Elwig/Helpers/Billing/Graph.cs
index da4f76b..7e213ea 100644
--- a/Elwig/Helpers/Billing/Graph.cs
+++ b/Elwig/Helpers/Billing/Graph.cs
@@ -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 {
diff --git a/Elwig/Helpers/Billing/PaymentBillingData.cs b/Elwig/Helpers/Billing/PaymentBillingData.cs
new file mode 100644
index 0000000..696258d
--- /dev/null
+++ b/Elwig/Helpers/Billing/PaymentBillingData.cs
@@ -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;
+        }
+    }
+}
diff --git a/Elwig/Windows/ChartWindow.xaml.cs b/Elwig/Windows/ChartWindow.xaml.cs
index bc745f8..c97bd34 100644
--- a/Elwig/Windows/ChartWindow.xaml.cs
+++ b/Elwig/Windows/ChartWindow.xaml.cs
@@ -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));