Implemented loading of old format.

This commit is contained in:
Tobias Erbshäußer
2020-06-18 18:34:06 +02:00
parent ee8d88146f
commit 55c9871d9a
10 changed files with 269 additions and 21 deletions
+3 -1
View File
@@ -58,8 +58,10 @@
LoadCompleted="OnCostsFrameLoadCompleted" LoadCompleted="OnCostsFrameLoadCompleted"
Focusable="False" /> Focusable="False" />
</TabItem> </TabItem>
<TabItem Visibility="Collapsed"> <TabItem Visibility="Collapsed"
Selector.Selected="OnDetailsTabSelected">
<Frame Source="Pages/DetailsPage.xaml" <Frame Source="Pages/DetailsPage.xaml"
x:Name="DetailsFrame"
Focusable="False" /> Focusable="False" />
</TabItem> </TabItem>
</TabControl> </TabControl>
+6 -1
View File
@@ -190,7 +190,12 @@ namespace UCalc
return false; return false;
} }
throw new System.NotImplementedException(); throw new NotImplementedException();
}
private void OnDetailsTabSelected(object sender, RoutedEventArgs e)
{
((DetailsPage) DetailsFrame.Content).Compute(Model);
} }
} }
} }
+231 -3
View File
@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@@ -70,11 +71,226 @@ namespace UCalc.Data
public class BillingLoader public class BillingLoader
{ {
[SuppressMessage("ReSharper", "PossibleNullReferenceException")] private static string AsString(JObject parent, string name, bool optional = false)
public Billing Load(string path)
{ {
// TODO: Support older format if (optional && (parent[name]?.Type ?? JTokenType.Null) == JTokenType.Null)
{
return "";
}
return parent[name]?.Value<string>() ??
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<int>() ??
throw new JsonException($"Cannot read {parent.Path}.{name} as integer");
}
private static bool AsBool(JObject parent, string name)
{
return parent[name]?.Value<bool>() ??
throw new JsonException($"Cannot read {parent.Path}.{name} as bool");
}
private static decimal AsDecimal(JObject parent, string name)
{
return decimal.Parse(parent[name]?.Value<string>() ??
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); var content = File.ReadAllText(path);
try 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) public void Store(string path, Billing billing)
{ {
File.WriteAllText(path, JsonConvert.SerializeObject(billing, Formatting.Indented)); File.WriteAllText(path, JsonConvert.SerializeObject(billing, Formatting.Indented));
+4
View File
@@ -9,10 +9,14 @@ namespace UCalc.Models
public CostEntriesProperty(Model model, Property parent, IEnumerable<CostEntry> data) : base(model, parent, public CostEntriesProperty(Model model, Property parent, IEnumerable<CostEntry> data) : base(model, parent,
"Zeiträume: Geben Sie einen oder mehr Zeiträume an.") "Zeiträume: Geben Sie einen oder mehr Zeiträume an.")
{ {
using var validator = Model.BeginValidation();
foreach (var entry in data) foreach (var entry in data)
{ {
Add(new CostEntryProperty(model, this, entry)); Add(new CostEntryProperty(model, this, entry));
} }
Modified = false;
} }
public void Add() public void Add()
+4
View File
@@ -160,10 +160,14 @@ namespace UCalc.Models
{ {
public DiscountsProperty(Model model, Property parent, IEnumerable<decimal> data) : base(model, parent) public DiscountsProperty(Model model, Property parent, IEnumerable<decimal> data) : base(model, parent)
{ {
using var validator = Model.BeginValidation();
foreach (var discount in data) foreach (var discount in data)
{ {
Add(new DiscountProperty(Model, this, "Abzug", discount)); Add(new DiscountProperty(Model, this, "Abzug", discount));
} }
Modified = false;
} }
public void Add() public void Add()
+1 -1
View File
@@ -86,7 +86,7 @@ namespace UCalc.Models
using var validator = Model.BeginValidation(); using var validator = Model.BeginValidation();
validator.Notify(this, "Errors"); validator.Notify(this, "Errors");
Model.Root.Costs.NotifyChanged((CostProperty) Parent); ((CostsProperty) Parent.Parent).NotifyChanged((CostProperty) Parent);
} }
public IEnumerator<FlatProperty> GetEnumerator() public IEnumerator<FlatProperty> GetEnumerator()
+4 -2
View File
@@ -15,12 +15,14 @@ namespace UCalc.Models
var error = ""; var error = "";
using var validator = Model.BeginValidation(); 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."; 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; return error;
} }
} }
+2 -8
View File
@@ -21,18 +21,12 @@ namespace UCalc.Models
public FlatProperty Add() public FlatProperty Add()
{ {
FlatProperty flat; using var validator = Model.BeginValidation(true);
{
using var validator = Model.BeginValidation();
flat = new FlatProperty(Model, this, new Flat {Name = $"Wohnung {Properties.Count + 1}"}); var flat = new FlatProperty(Model, this, new Flat {Name = $"Wohnung {Properties.Count + 1}"});
base.Add(flat); base.Add(flat);
}
{
using var validator = Model.BeginValidation();
validator.Validate(flat); validator.Validate(flat);
}
return flat; return flat;
} }
+4 -2
View File
@@ -105,6 +105,8 @@ namespace UCalc.Models
{ {
_errors.Clear(); _errors.Clear();
var tenants = (TenantsProperty) Parent.Parent;
try try
{ {
if (_flatProperties.Count == 0) if (_flatProperties.Count == 0)
@@ -118,7 +120,7 @@ namespace UCalc.Models
foreach (var flatProperty in _flatProperties) foreach (var flatProperty in _flatProperties)
{ {
foreach (var tenant in Model.Root.Tenants) foreach (var tenant in tenants)
{ {
if (ReferenceEquals(this, tenant.RentedFlats)) if (ReferenceEquals(this, tenant.RentedFlats))
{ {
@@ -158,7 +160,7 @@ namespace UCalc.Models
using var validator = Model.BeginValidation(); using var validator = Model.BeginValidation();
validator.Notify(this, "Errors"); validator.Notify(this, "Errors");
validator.ValidateRange(Model.Root.Tenants); validator.ValidateRange(tenants);
} }
} }
+8 -1
View File
@@ -1,4 +1,6 @@
using System.Windows.Controls; using System;
using System.Windows.Controls;
using UCalc.Models;
namespace UCalc.Pages namespace UCalc.Pages
{ {
@@ -8,5 +10,10 @@ namespace UCalc.Pages
{ {
InitializeComponent(); InitializeComponent();
} }
public void Compute(Model model)
{
throw new NotImplementedException();
}
} }
} }