From 55c9871d9a3a0017d463d478028b9b5174e7a938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Erbsh=C3=A4u=C3=9Fer?= Date: Thu, 18 Jun 2020 18:34:06 +0200 Subject: [PATCH] Implemented loading of old format. --- ucalc/BillingWindow.xaml | 4 +- ucalc/BillingWindow.xaml.cs | 7 +- ucalc/Data/BillingLoader.cs | 234 +++++++++++++++++++++++++++- ucalc/Models/CostEntriesProperty.cs | 4 + ucalc/Models/CostEntryProperty.cs | 4 + ucalc/Models/CostProperty.cs | 2 +- ucalc/Models/FlatProperty.cs | 6 +- ucalc/Models/FlatsProperty.cs | 14 +- ucalc/Models/TenantProperty.cs | 6 +- ucalc/Pages/DetailsPage.xaml.cs | 9 +- 10 files changed, 269 insertions(+), 21 deletions(-) diff --git a/ucalc/BillingWindow.xaml b/ucalc/BillingWindow.xaml index 3cc2d58..333cd37 100644 --- a/ucalc/BillingWindow.xaml +++ b/ucalc/BillingWindow.xaml @@ -58,8 +58,10 @@ LoadCompleted="OnCostsFrameLoadCompleted" Focusable="False" /> - + diff --git a/ucalc/BillingWindow.xaml.cs b/ucalc/BillingWindow.xaml.cs index fd72605..7dd6643 100644 --- a/ucalc/BillingWindow.xaml.cs +++ b/ucalc/BillingWindow.xaml.cs @@ -190,7 +190,12 @@ namespace UCalc return false; } - throw new System.NotImplementedException(); + throw new NotImplementedException(); + } + + private void OnDetailsTabSelected(object sender, RoutedEventArgs e) + { + ((DetailsPage) DetailsFrame.Content).Compute(Model); } } } \ No newline at end of file diff --git a/ucalc/Data/BillingLoader.cs b/ucalc/Data/BillingLoader.cs index f1be8fb..a14c7e7 100644 --- a/ucalc/Data/BillingLoader.cs +++ b/ucalc/Data/BillingLoader.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -70,11 +71,226 @@ namespace UCalc.Data public class BillingLoader { - [SuppressMessage("ReSharper", "PossibleNullReferenceException")] - public Billing Load(string path) + private static string AsString(JObject parent, string name, bool optional = false) { - // TODO: Support older format + if (optional && (parent[name]?.Type ?? JTokenType.Null) == JTokenType.Null) + { + return ""; + } + return parent[name]?.Value() ?? + throw new JsonException($"Cannot read {parent.Path}.{name} as string"); + } + + private static DateTime AsDate(JObject parent, string name) + { + return DateTime.ParseExact(AsString(parent, name), "dd.MM.yyyy", null); + } + + private static DateTime? AsDateOptional(JObject parent, string name) + { + var str = AsString(parent, name, true); + if (string.IsNullOrEmpty(str)) + { + return null; + } + + return DateTime.ParseExact(str, "dd.MM.yyyy", null); + } + + private static int AsInt(JObject parent, string name) + { + return parent[name]?.Value() ?? + throw new JsonException($"Cannot read {parent.Path}.{name} as integer"); + } + + private static bool AsBool(JObject parent, string name) + { + return parent[name]?.Value() ?? + throw new JsonException($"Cannot read {parent.Path}.{name} as bool"); + } + + private static decimal AsDecimal(JObject parent, string name) + { + return decimal.Parse(parent[name]?.Value() ?? + throw new JsonException($"Cannot read {parent.Path}.{name} as decimal")); + } + + private static decimal? AsDecimalOptional(JObject parent, string name) + { + var str = AsString(parent, name, true); + if (string.IsNullOrEmpty(str)) + { + return null; + } + + return decimal.Parse(str); + } + + private static JObject AsObject(JObject parent, string name) + { + if ((parent[name]?.Type ?? JTokenType.Null) != JTokenType.Object) + { + throw new JsonException($"Cannot read {parent.Path}.{name} as object"); + } + + return (JObject) parent[name]; + } + + private static JArray AsArray(JObject parent, string name, bool optional = false) + { + var type = parent[name]?.Type ?? JTokenType.Object; + + if (optional && type == JTokenType.Null) + { + return null; + } + + if (type != JTokenType.Array) + { + throw new JsonException($"Cannot read {parent.Path}.{name} as array"); + } + + return (JArray) parent[name]; + } + + private static void LoadAddress(JObject parent, string name, Address target) + { + var address = AsObject(parent, name); + target.Street = AsString(address, "street"); + target.HouseNumber = AsString(address, "house_number"); + target.City = AsString(address, "city"); + target.Postcode = AsString(address, "plz"); + } + + private static void LoadBankAccount(JObject parent, string name, BankAccount target) + { + var bankAccount = AsObject(parent, name); + target.Iban = AsString(bankAccount, "iban"); + target.Bic = AsString(bankAccount, "bic"); + target.BankName = AsString(bankAccount, "bank_name"); + } + + private Billing LoadFormat1(string path) + { + var content = File.ReadAllText(path); + try + { + var root = JObject.Parse(content); + var billing = new Billing + { + StartDate = AsDate(root, "start_date"), + EndDate = AsDate(root, "end_date") + }; + + var landlord = AsObject(root, "owner"); + billing.Landlord.Salutation = (Salutation) AsInt(landlord, "salutation"); + billing.Landlord.Name = AsString(landlord, "name"); + billing.Landlord.Phone = AsString(landlord, "phone"); + billing.Landlord.MailAddress = AsString(landlord, "mail"); + LoadAddress(landlord, "address", billing.Landlord.Address); + LoadBankAccount(landlord, "account", billing.Landlord.BankAccount); + + var house = AsObject(root, "house"); + LoadAddress(house, "address", billing.House.Address); + + var tenants = AsArray(root, "renters"); + foreach (var item in tenants) + { + var tenant = (JObject) item; + + var targetTenant = new Tenant + { + Salutation = (Salutation) AsInt(tenant, "salutation"), + Name = AsString(tenant, "name"), + PersonCount = AsInt(tenant, "person_count"), + EntryDate = AsDateOptional(tenant, "entry_date"), + DepartureDate = AsDateOptional(tenant, "departure_date"), + PaidRent = AsDecimal(tenant, "paid_rent"), + CustomMessage1 = AsString(tenant, "message1", true), + CustomMessage2 = AsString(tenant, "message2", true) + }; + LoadBankAccount(tenant, "account", targetTenant.BankAccount); + + var flat = new Flat + { + Name = $"Wohnung von {targetTenant.Name}", + Size = 1 + }; + + targetTenant.RentedFlats.Add(flat); + + billing.Tenants.Add(targetTenant); + billing.House.Flats.Add(flat); + } + + var costs = AsArray(root, "costs"); + foreach (var item in costs) + { + var cost = (JObject) item; + + var targetCost = new Cost + { + Name = AsString(cost, "name"), + Division = (CostDivision) AsInt(cost, "division"), + AffectsAll = AsBool(cost, "affects_all_renters"), + AffectedFlats = + (AsArray(cost, "affected_renters", true) ?? new JArray()).Select( + renterItem => + billing.House.Flats[AsInt((JObject) renterItem, "id")]).ToHashSet(), + Entries = (AsArray(cost, "entries", true) ?? new JArray()).Select(entryItem => + { + var parent = (JObject) entryItem; + + var details = new CostEntryDetails + { + TotalAmount = AsDecimalOptional(parent, "cubic.sumprice") ?? 0, + UnitCount = AsDecimalOptional(parent, "cubic.sum") ?? 0 + }; + + for (var i = 1; i <= 4; ++i) + { + var d = AsDecimalOptional(parent, $"cubic.discount{i}"); + + if (d != null) + { + details.DiscountsInUnits.Add(d.Value); + } + } + + return new CostEntry + { + StartDate = AsDate(parent, "start_date"), + EndDate = AsDate(parent, "end_date"), + Amount = AsDecimal(parent, "price"), + Details = details + }; + }).ToList(), + DisplayInBill = AsBool(cost, "display") + }; + + billing.Costs.Add(targetCost); + } + + return billing; + } + catch (JsonException e) + { + throw new IOException($"Could not load JSON with error:\n{e.Message}", e); + } + catch (InvalidCastException e) + { + throw new IOException($"Could not load JSON with error:\n{e.Message}", e); + } + catch (FormatException e) + { + throw new IOException($"Could not load JSON with error:\n{e.Message}", e); + } + } + + [SuppressMessage("ReSharper", "PossibleNullReferenceException")] + private Billing LoadFormat2(string path) + { var content = File.ReadAllText(path); try { @@ -121,6 +337,18 @@ namespace UCalc.Data } } + public Billing Load(string path) + { + try + { + return LoadFormat2(path); + } + catch + { + return LoadFormat1(path); + } + } + public void Store(string path, Billing billing) { File.WriteAllText(path, JsonConvert.SerializeObject(billing, Formatting.Indented)); diff --git a/ucalc/Models/CostEntriesProperty.cs b/ucalc/Models/CostEntriesProperty.cs index 0f84bc9..76bed2a 100644 --- a/ucalc/Models/CostEntriesProperty.cs +++ b/ucalc/Models/CostEntriesProperty.cs @@ -9,10 +9,14 @@ namespace UCalc.Models public CostEntriesProperty(Model model, Property parent, IEnumerable data) : base(model, parent, "Zeiträume: Geben Sie einen oder mehr Zeiträume an.") { + using var validator = Model.BeginValidation(); + foreach (var entry in data) { Add(new CostEntryProperty(model, this, entry)); } + + Modified = false; } public void Add() diff --git a/ucalc/Models/CostEntryProperty.cs b/ucalc/Models/CostEntryProperty.cs index 2bd21bc..94fc6bd 100644 --- a/ucalc/Models/CostEntryProperty.cs +++ b/ucalc/Models/CostEntryProperty.cs @@ -160,10 +160,14 @@ namespace UCalc.Models { public DiscountsProperty(Model model, Property parent, IEnumerable data) : base(model, parent) { + using var validator = Model.BeginValidation(); + foreach (var discount in data) { Add(new DiscountProperty(Model, this, "Abzug", discount)); } + + Modified = false; } public void Add() diff --git a/ucalc/Models/CostProperty.cs b/ucalc/Models/CostProperty.cs index fc67d2d..cbc9f9f 100644 --- a/ucalc/Models/CostProperty.cs +++ b/ucalc/Models/CostProperty.cs @@ -86,7 +86,7 @@ namespace UCalc.Models using var validator = Model.BeginValidation(); validator.Notify(this, "Errors"); - Model.Root.Costs.NotifyChanged((CostProperty) Parent); + ((CostsProperty) Parent.Parent).NotifyChanged((CostProperty) Parent); } public IEnumerator GetEnumerator() diff --git a/ucalc/Models/FlatProperty.cs b/ucalc/Models/FlatProperty.cs index f8f3027..9261632 100644 --- a/ucalc/Models/FlatProperty.cs +++ b/ucalc/Models/FlatProperty.cs @@ -15,12 +15,14 @@ namespace UCalc.Models var error = ""; using var validator = Model.BeginValidation(); - if (Model.Root.House.Flats.Any(flat => !ReferenceEquals(this, flat.Name) && flat.Name.Value == Value)) + var flats = (FlatsProperty) Parent.Parent; + + if (flats.Any(flat => !ReferenceEquals(this, flat.Name) && flat.Name.Value == Value)) { error = $"{Name}: Der Name ist nicht eindeutig."; } - validator.ValidateRange(Model.Root.House.Flats.Select(flat => flat.Name)); + validator.ValidateRange(flats.Select(flat => flat.Name)); return error; } } diff --git a/ucalc/Models/FlatsProperty.cs b/ucalc/Models/FlatsProperty.cs index 6216d22..8120bc8 100644 --- a/ucalc/Models/FlatsProperty.cs +++ b/ucalc/Models/FlatsProperty.cs @@ -21,18 +21,12 @@ namespace UCalc.Models public FlatProperty Add() { - FlatProperty flat; - { - using var validator = Model.BeginValidation(); + using var validator = Model.BeginValidation(true); - flat = new FlatProperty(Model, this, new Flat {Name = $"Wohnung {Properties.Count + 1}"}); - base.Add(flat); - } + var flat = new FlatProperty(Model, this, new Flat {Name = $"Wohnung {Properties.Count + 1}"}); + base.Add(flat); - { - using var validator = Model.BeginValidation(); - validator.Validate(flat); - } + validator.Validate(flat); return flat; } diff --git a/ucalc/Models/TenantProperty.cs b/ucalc/Models/TenantProperty.cs index 1dbedcb..0f38a57 100644 --- a/ucalc/Models/TenantProperty.cs +++ b/ucalc/Models/TenantProperty.cs @@ -105,6 +105,8 @@ namespace UCalc.Models { _errors.Clear(); + var tenants = (TenantsProperty) Parent.Parent; + try { if (_flatProperties.Count == 0) @@ -118,7 +120,7 @@ namespace UCalc.Models foreach (var flatProperty in _flatProperties) { - foreach (var tenant in Model.Root.Tenants) + foreach (var tenant in tenants) { if (ReferenceEquals(this, tenant.RentedFlats)) { @@ -158,7 +160,7 @@ namespace UCalc.Models using var validator = Model.BeginValidation(); validator.Notify(this, "Errors"); - validator.ValidateRange(Model.Root.Tenants); + validator.ValidateRange(tenants); } } diff --git a/ucalc/Pages/DetailsPage.xaml.cs b/ucalc/Pages/DetailsPage.xaml.cs index 4f1e050..0202112 100644 --- a/ucalc/Pages/DetailsPage.xaml.cs +++ b/ucalc/Pages/DetailsPage.xaml.cs @@ -1,4 +1,6 @@ -using System.Windows.Controls; +using System; +using System.Windows.Controls; +using UCalc.Models; namespace UCalc.Pages { @@ -8,5 +10,10 @@ namespace UCalc.Pages { InitializeComponent(); } + + public void Compute(Model model) + { + throw new NotImplementedException(); + } } } \ No newline at end of file