Implemented loading of old format.
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -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));
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user