Implemented loading of new format.

This commit is contained in:
Tobias Erbshäußer
2020-06-16 18:34:27 +02:00
parent 1a94bb0692
commit 74205f016a
10 changed files with 366 additions and 34 deletions
+109 -15
View File
@@ -2,6 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
#pragma warning disable 659 #pragma warning disable 659
@@ -43,9 +45,17 @@ namespace UCalc.Data
public class Address public class Address
{ {
[JsonProperty(PropertyName = "street"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Street { get; set; } public string Street { get; set; }
[JsonProperty(PropertyName = "houseNumber"), JsonRequired,
JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string HouseNumber { get; set; } public string HouseNumber { get; set; }
[JsonProperty(PropertyName = "city"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string City { get; set; } public string City { get; set; }
[JsonProperty(PropertyName = "postCode"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Postcode { get; set; } public string Postcode { get; set; }
public Address() public Address()
@@ -83,8 +93,13 @@ namespace UCalc.Data
public class BankAccount public class BankAccount
{ {
[JsonProperty(PropertyName = "iban"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Iban { get; set; } public string Iban { get; set; }
[JsonProperty(PropertyName = "bic"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Bic { get; set; } public string Bic { get; set; }
[JsonProperty(PropertyName = "bankName"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string BankName { get; set; } public string BankName { get; set; }
public BankAccount() public BankAccount()
@@ -119,8 +134,13 @@ namespace UCalc.Data
public class Flat public class Flat
{ {
public Guid Id { get; private set; } [JsonProperty(PropertyName = "id"), JsonRequired]
public Guid Id { get; set; }
[JsonProperty(PropertyName = "name"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Name { get; set; } public string Name { get; set; }
[JsonProperty(PropertyName = "size"), JsonRequired]
public int Size { get; set; } public int Size { get; set; }
public Flat() public Flat()
@@ -161,16 +181,37 @@ namespace UCalc.Data
public class Tenant public class Tenant
{ {
public Guid Id { get; private set; } [JsonProperty(PropertyName = "id"), JsonRequired]
public Guid Id { get; set; }
[JsonProperty(PropertyName = "salutation"), JsonRequired, JsonConverter(typeof(StringEnumConverter))]
public Salutation Salutation { get; set; } public Salutation Salutation { get; set; }
[JsonProperty(PropertyName = "name"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Name { get; set; } public string Name { get; set; }
[JsonProperty(PropertyName = "personCount"), JsonRequired]
public int PersonCount { get; set; } public int PersonCount { get; set; }
public BankAccount BankAccount { get; private set; }
[JsonProperty(PropertyName = "bankAccount"), JsonRequired]
public BankAccount BankAccount { get; set; }
[JsonProperty(PropertyName = "entryDate")]
public DateTime? EntryDate { get; set; } public DateTime? EntryDate { get; set; }
[JsonProperty(PropertyName = "departureDate")]
public DateTime? DepartureDate { get; set; } public DateTime? DepartureDate { get; set; }
public HashSet<Flat> RentedFlats { get; private set; }
[JsonProperty(PropertyName = "rentedFlats"), JsonRequired, JsonConverter(typeof(FlatSerializationConverter))]
public HashSet<Flat> RentedFlats { get; set; }
[JsonProperty(PropertyName = "paidRent"), JsonRequired]
public decimal PaidRent { get; set; } public decimal PaidRent { get; set; }
[JsonProperty(PropertyName = "customMessage1"), JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string CustomMessage1 { get; set; } public string CustomMessage1 { get; set; }
[JsonProperty(PropertyName = "customMessage2"), JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string CustomMessage2 { get; set; } public string CustomMessage2 { get; set; }
public Tenant() public Tenant()
@@ -179,6 +220,8 @@ namespace UCalc.Data
Name = ""; Name = "";
BankAccount = new BankAccount(); BankAccount = new BankAccount();
RentedFlats = new HashSet<Flat>(); RentedFlats = new HashSet<Flat>();
CustomMessage1 = "";
CustomMessage2 = "";
} }
private bool Equals(Tenant other) private bool Equals(Tenant other)
@@ -218,12 +261,24 @@ namespace UCalc.Data
public class Landlord public class Landlord
{ {
[JsonProperty(PropertyName = "salutation"), JsonRequired, JsonConverter(typeof(StringEnumConverter))]
public Salutation Salutation { get; set; } public Salutation Salutation { get; set; }
[JsonProperty(PropertyName = "name"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Name { get; set; } public string Name { get; set; }
[JsonProperty(PropertyName = "mailAddress"), JsonRequired,
JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string MailAddress { get; set; } public string MailAddress { get; set; }
[JsonProperty(PropertyName = "phone"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Phone { get; set; } public string Phone { get; set; }
public Address Address { get; private set; }
public BankAccount BankAccount { get; private set; } [JsonProperty(PropertyName = "address"), JsonRequired]
public Address Address { get; set; }
[JsonProperty(PropertyName = "bankAccount"), JsonRequired]
public BankAccount BankAccount { get; set; }
public Landlord() public Landlord()
{ {
@@ -263,8 +318,11 @@ namespace UCalc.Data
public class House public class House
{ {
public Address Address { get; private set; } [JsonProperty(PropertyName = "address"), JsonRequired]
public List<Flat> Flats { get; private set; } public Address Address { get; set; }
[JsonProperty(PropertyName = "flats"), JsonRequired]
public List<Flat> Flats { get; set; }
public House() public House()
{ {
@@ -296,9 +354,14 @@ namespace UCalc.Data
public class CostEntryDetails public class CostEntryDetails
{ {
[JsonProperty(PropertyName = "totalAmount"), JsonRequired]
public decimal TotalAmount { get; set; } public decimal TotalAmount { get; set; }
[JsonProperty(PropertyName = "unitCount"), JsonRequired]
public decimal UnitCount { get; set; } public decimal UnitCount { get; set; }
public List<decimal> DiscountsInUnits { get; private set; }
[JsonProperty(PropertyName = "discountsInUnits"), JsonRequired]
public List<decimal> DiscountsInUnits { get; set; }
public CostEntryDetails() public CostEntryDetails()
{ {
@@ -331,9 +394,16 @@ namespace UCalc.Data
public class CostEntry public class CostEntry
{ {
[JsonProperty(PropertyName = "startDate"), JsonRequired]
public DateTime StartDate { get; set; } public DateTime StartDate { get; set; }
[JsonProperty(PropertyName = "endDate"), JsonRequired]
public DateTime EndDate { get; set; } public DateTime EndDate { get; set; }
[JsonProperty(PropertyName = "amount"), JsonRequired]
public decimal Amount { get; set; } public decimal Amount { get; set; }
[JsonProperty(PropertyName = "details"), JsonRequired]
public CostEntryDetails Details { get; set; } public CostEntryDetails Details { get; set; }
public CostEntry() public CostEntry()
@@ -393,12 +463,25 @@ namespace UCalc.Data
public class Cost public class Cost
{ {
[JsonProperty(PropertyName = "name"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Name { get; set; } public string Name { get; set; }
[JsonProperty(PropertyName = "division"), JsonRequired, JsonConverter(typeof(StringEnumConverter))]
public CostDivision Division { get; set; } public CostDivision Division { get; set; }
[JsonProperty(PropertyName = "affectsAll"), JsonRequired]
public bool AffectsAll { get; set; } public bool AffectsAll { get; set; }
[JsonProperty(PropertyName = "includeUnrented"), JsonRequired]
public bool IncludeUnrented { get; set; } public bool IncludeUnrented { get; set; }
public HashSet<Flat> AffectedFlats { get; private set; }
public List<CostEntry> Entries { get; private set; } [JsonProperty(PropertyName = "affectedFlats"), JsonRequired, JsonConverter(typeof(FlatSerializationConverter))]
public HashSet<Flat> AffectedFlats { get; set; }
[JsonProperty(PropertyName = "entries"), JsonRequired]
public List<CostEntry> Entries { get; set; }
[JsonProperty(PropertyName = "displayInBill"), JsonRequired]
public bool DisplayInBill { get; set; } public bool DisplayInBill { get; set; }
public Cost() public Cost()
@@ -439,12 +522,23 @@ namespace UCalc.Data
public class Billing public class Billing
{ {
[JsonProperty(PropertyName = "startDate"), JsonRequired]
public DateTime StartDate { get; set; } public DateTime StartDate { get; set; }
[JsonProperty(PropertyName = "endDate"), JsonRequired]
public DateTime EndDate { get; set; } public DateTime EndDate { get; set; }
public Landlord Landlord { get; private set; }
public House House { get; private set; } [JsonProperty(PropertyName = "landlord"), JsonRequired]
public List<Tenant> Tenants { get; private set; } public Landlord Landlord { get; set; }
public List<Cost> Costs { get; private set; }
[JsonProperty(PropertyName = "house"), JsonRequired]
public House House { get; set; }
[JsonProperty(PropertyName = "tenants"), JsonRequired]
public List<Tenant> Tenants { get; set; }
[JsonProperty(PropertyName = "costs"), JsonRequired]
public List<Cost> Costs { get; set; }
public Billing() public Billing()
{ {
+114 -3
View File
@@ -1,18 +1,129 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace UCalc.Data namespace UCalc.Data
{ {
internal class FlatSerializationConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var set = (HashSet<Flat>) value;
writer.WriteStartArray();
foreach (var flat in set)
{
writer.WriteValue(flat.Id.ToString());
}
writer.WriteEndArray();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartArray)
{
throw new JsonException("Expected [");
}
while (reader.Read() && reader.TokenType == JsonToken.String)
{
}
if (reader.TokenType != JsonToken.EndArray)
{
throw new JsonException("Expected ]");
}
return new HashSet<Flat>();
}
public override bool CanConvert(Type objectType)
{
throw new InvalidOperationException();
}
}
internal class JsonNullToEmptyStringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
return existingValue ?? string.Empty;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value ?? string.Empty);
}
}
public class BillingLoader public class BillingLoader
{ {
[SuppressMessage("ReSharper", "PossibleNullReferenceException")]
public Billing Load(string path) public Billing Load(string path)
{ {
// TODO // TODO: Support older format
return new Billing();
var content = File.ReadAllText(path);
try
{
var billing = JsonConvert.DeserializeObject<Billing>(content);
var idToFlat = new Dictionary<string, Flat>();
foreach (var flat in billing.House.Flats)
{
idToFlat.Add(flat.Id.ToString(), flat);
}
var root = JObject.Parse(content);
var i = 0;
foreach (var tenantItem in root["tenants"])
{
foreach (var rentedFlatId in tenantItem["rentedFlats"])
{
billing.Tenants[i].RentedFlats.Add(idToFlat[rentedFlatId.Value<string>()]);
}
++i;
}
i = 0;
foreach (var tenantItem in root["costs"])
{
foreach (var rentedFlatId in tenantItem["affectedFlats"])
{
billing.Costs[i].AffectedFlats.Add(idToFlat[rentedFlatId.Value<string>()]);
}
++i;
}
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);
}
} }
public void Store(string path, Billing billing) public void Store(string path, Billing billing)
{ {
throw new NotImplementedException(); File.WriteAllText(path, JsonConvert.SerializeObject(billing, Formatting.Indented));
} }
} }
} }
+4 -4
View File
@@ -1,5 +1,4 @@
using System; using System.IO;
using System.IO;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
@@ -78,9 +77,10 @@ namespace UCalc
new BillingWindow(path, billing).Show(); new BillingWindow(path, billing).Show();
Hide(); Hide();
} }
catch (IOException) catch (IOException e)
{ {
throw new NotImplementedException(); MessageBox.Show($"Beim Laden der Datei \"{path}\" ist ein Fehler aufgetreten!\n\nDetails: {e.Message}",
"Fehler!", MessageBoxButton.OK, MessageBoxImage.Error);
} }
} }
} }
+4 -1
View File
@@ -51,7 +51,10 @@ namespace UCalc.Models
tenant.RentedFlats.Remove(flat); tenant.RentedFlats.Remove(flat);
} }
// TODO: Revalidate all flats + revalidate tenant rent lists + costs flat lists foreach (var cost in Model.Root.Costs)
{
cost.AffectedFlats.Remove(flat);
}
} }
} }
} }
+97 -3
View File
@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using UCalc.Annotations; using UCalc.Annotations;
using UCalc.Data; using UCalc.Data;
@@ -48,7 +49,7 @@ namespace UCalc.Models
return; return;
} }
if (_validated.Add(property)) if (_validated.Add(property) && !_model._loading)
{ {
property.Validate(); property.Validate();
} }
@@ -106,6 +107,18 @@ namespace UCalc.Models
return -1; return -1;
} }
var d1 = prop1.DepthInTree();
var d2 = prop2.DepthInTree();
if (d1 < d2)
{
return 1;
}
if (d1 > d2)
{
return -1;
}
return 0; return 0;
}); });
@@ -127,6 +140,7 @@ namespace UCalc.Models
} }
private readonly Validator _validator; private readonly Validator _validator;
private readonly bool _loading;
public DateTime StartDate { get; } public DateTime StartDate { get; }
public DateTime EndDate { get; } public DateTime EndDate { get; }
public BillingProperty Root { get; } public BillingProperty Root { get; }
@@ -137,8 +151,12 @@ namespace UCalc.Models
StartDate = billing.StartDate; StartDate = billing.StartDate;
EndDate = billing.EndDate; EndDate = billing.EndDate;
using var validator = BeginValidation(); _loading = true;
Root = new BillingProperty(this, null, billing); Root = new BillingProperty(this, null, billing);
_loading = false;
using var validator = BeginValidation();
validator.Validate(Root);
} }
public Validator BeginValidation() public Validator BeginValidation()
@@ -149,7 +167,83 @@ namespace UCalc.Models
public Billing Dump() public Billing Dump()
{ {
throw new NotImplementedException(); var billing = new Billing
{
StartDate = StartDate,
EndDate = EndDate,
Landlord =
{
Salutation = (Salutation) Root.Landlord.Salutation.Value,
Name = Root.Landlord.Name.Value,
MailAddress = Root.Landlord.MailAddress.Value,
Phone = Root.Landlord.Phone.Value,
Address =
{
Street = Root.Landlord.Address.Street.Value,
HouseNumber = Root.Landlord.Address.HouseNumber.Value,
City = Root.Landlord.Address.City.Value,
Postcode = Root.Landlord.Address.Postcode.Value
},
BankAccount =
{
Iban = Root.Landlord.BankAccount.Iban.Value,
Bic = Root.Landlord.BankAccount.Bic.Value,
BankName = Root.Landlord.BankAccount.BankName.Value
}
},
House =
{
Address =
{
Street = Root.House.Address.Street.Value,
HouseNumber = Root.House.Address.HouseNumber.Value,
City = Root.House.Address.City.Value,
Postcode = Root.House.Address.Postcode.Value
},
Flats = Root.House.Flats.Select(flat => new Flat
{Name = flat.Name.Value, Size = int.TryParse(flat.Size.Value, out var n) ? n : 0}).ToList()
}
};
var flatPropertyToFlat = new Dictionary<FlatProperty, Flat>();
for (var i = 0; i < Root.House.Flats.Count; ++i)
{
flatPropertyToFlat.Add(Root.House.Flats[i], billing.House.Flats[i]);
}
billing.Tenants = Root.Tenants.Select(tenant => new Tenant
{
Salutation = (Salutation) tenant.Salutation.Value,
Name = tenant.Name.Value,
PersonCount = int.TryParse(tenant.PersonCount.Value, out var n) ? n : 0,
BankAccount =
{
Iban = tenant.BankAccount.Iban.Value,
Bic = tenant.BankAccount.Bic.Value,
BankName = tenant.BankAccount.BankName.Value
},
EntryDate = tenant.EntryDate.Value,
DepartureDate = tenant.DepartureDate.Value,
RentedFlats =
new HashSet<Flat>(tenant.RentedFlats.Select(rentedFlat => flatPropertyToFlat[rentedFlat])),
PaidRent = decimal.TryParse(tenant.PaidRent.Value, out var d) ? d : 0,
CustomMessage1 = tenant.CustomMessage1.Value,
CustomMessage2 = tenant.CustomMessage2.Value
}).ToList();
billing.Costs = Root.Costs.Select(cost => new Cost
{
Name = cost.Name.Value,
Division = (CostDivision) cost.Division.Value,
AffectsAll = cost.AffectsAll.Value,
IncludeUnrented = cost.IncludeUnrented.Value,
AffectedFlats =
new HashSet<Flat>(cost.AffectedFlats.Select(rentedFlat => flatPropertyToFlat[rentedFlat])),
// TODO: Entries = {},
DisplayInBill = cost.DisplayInBill.Value
}).ToList();
return billing;
} }
public void ResetModified() public void ResetModified()
+15 -1
View File
@@ -67,11 +67,25 @@ namespace UCalc.Models
return false; return false;
} }
public int DepthInTree()
{
var i = 0;
var property = Parent;
while (property != null)
{
++i;
property = property.Parent;
}
return i;
}
} }
public abstract class ValueProperty<T> : Property public abstract class ValueProperty<T> : Property
{ {
protected string Name; protected readonly string Name;
private T _value; private T _value;
private readonly List<string> _errors; private readonly List<string> _errors;
+16 -2
View File
@@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UCalc.Controls; using UCalc.Controls;
using UCalc.Data; using UCalc.Data;
@@ -48,7 +49,7 @@ namespace UCalc.Models
} }
} }
public class RentedFlatsProperty : Property public class RentedFlatsProperty : Property, IReadOnlyCollection<FlatProperty>
{ {
private const string NoFlatsError = "Gemietete Wohnungen: Es wurde keine Wohnung zugewiesen."; private const string NoFlatsError = "Gemietete Wohnungen: Es wurde keine Wohnung zugewiesen.";
private readonly HashSet<FlatProperty> _flatProperties; private readonly HashSet<FlatProperty> _flatProperties;
@@ -65,7 +66,8 @@ namespace UCalc.Models
_flatProperties.Add(flatToProperty?[flat] ?? throw new InvalidOperationException()); _flatProperties.Add(flatToProperty?[flat] ?? throw new InvalidOperationException());
} }
Validate(); using var validator = Model.BeginValidation();
validator.Validate(this);
} }
public override IReadOnlyList<string> Errors => _errors; public override IReadOnlyList<string> Errors => _errors;
@@ -175,6 +177,18 @@ namespace UCalc.Models
return thisStart.Intersects(thisEnd, otherStart, otherEnd); return thisStart.Intersects(thisEnd, otherStart, otherEnd);
} }
public IEnumerator<FlatProperty> GetEnumerator()
{
return _flatProperties.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int Count => _flatProperties.Count;
} }
public class TenantProperty : NestedProperty public class TenantProperty : NestedProperty
-2
View File
@@ -13,8 +13,6 @@
PreviewMouseUp="OnPreviewMouseUp" PreviewMouseUp="OnPreviewMouseUp"
Icon="logo.ico"> Icon="logo.ico">
<!-- TODO: Wrap in scroll to handle small screens! -->
<StackPanel Margin="12"> <StackPanel Margin="12">
<Label Content="Geben Sie den Zeitraum ein, für den die neue Abrechnung gelten soll." <Label Content="Geben Sie den Zeitraum ein, für den die neue Abrechnung gelten soll."
+3 -3
View File
@@ -38,17 +38,17 @@ namespace UCalc.Pages
} }
} }
public void OnSaveClick(object sender, RoutedEventArgs e) private void OnSaveClick(object sender, RoutedEventArgs e)
{ {
ParentWindow.Save(); ParentWindow.Save();
} }
public void OnPrintClick(object sender, RoutedEventArgs e) private void OnPrintClick(object sender, RoutedEventArgs e)
{ {
ParentWindow.Print(); ParentWindow.Print();
} }
public void OnAboutClick(object sender, RoutedEventArgs e) private void OnAboutClick(object sender, RoutedEventArgs e)
{ {
throw new System.NotImplementedException(); throw new System.NotImplementedException();
} }
+4
View File
@@ -95,4 +95,8 @@
<Resource Include="logo.ico"/> <Resource Include="logo.ico"/>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3"/>
</ItemGroup>
</Project> </Project>