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.Linq;
using System.Runtime.CompilerServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
#pragma warning disable 659
@@ -43,9 +45,17 @@ namespace UCalc.Data
public class Address
{
[JsonProperty(PropertyName = "street"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Street { get; set; }
[JsonProperty(PropertyName = "houseNumber"), JsonRequired,
JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string HouseNumber { get; set; }
[JsonProperty(PropertyName = "city"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string City { get; set; }
[JsonProperty(PropertyName = "postCode"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Postcode { get; set; }
public Address()
@@ -83,8 +93,13 @@ namespace UCalc.Data
public class BankAccount
{
[JsonProperty(PropertyName = "iban"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Iban { get; set; }
[JsonProperty(PropertyName = "bic"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Bic { get; set; }
[JsonProperty(PropertyName = "bankName"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string BankName { get; set; }
public BankAccount()
@@ -119,8 +134,13 @@ namespace UCalc.Data
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; }
[JsonProperty(PropertyName = "size"), JsonRequired]
public int Size { get; set; }
public Flat()
@@ -161,16 +181,37 @@ namespace UCalc.Data
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; }
[JsonProperty(PropertyName = "name"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Name { get; set; }
[JsonProperty(PropertyName = "personCount"), JsonRequired]
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; }
[JsonProperty(PropertyName = "departureDate")]
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; }
[JsonProperty(PropertyName = "customMessage1"), JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string CustomMessage1 { get; set; }
[JsonProperty(PropertyName = "customMessage2"), JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string CustomMessage2 { get; set; }
public Tenant()
@@ -179,6 +220,8 @@ namespace UCalc.Data
Name = "";
BankAccount = new BankAccount();
RentedFlats = new HashSet<Flat>();
CustomMessage1 = "";
CustomMessage2 = "";
}
private bool Equals(Tenant other)
@@ -218,12 +261,24 @@ namespace UCalc.Data
public class Landlord
{
[JsonProperty(PropertyName = "salutation"), JsonRequired, JsonConverter(typeof(StringEnumConverter))]
public Salutation Salutation { get; set; }
[JsonProperty(PropertyName = "name"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Name { get; set; }
[JsonProperty(PropertyName = "mailAddress"), JsonRequired,
JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string MailAddress { get; set; }
[JsonProperty(PropertyName = "phone"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
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()
{
@@ -263,8 +318,11 @@ namespace UCalc.Data
public class House
{
public Address Address { get; private set; }
public List<Flat> Flats { get; private set; }
[JsonProperty(PropertyName = "address"), JsonRequired]
public Address Address { get; set; }
[JsonProperty(PropertyName = "flats"), JsonRequired]
public List<Flat> Flats { get; set; }
public House()
{
@@ -296,9 +354,14 @@ namespace UCalc.Data
public class CostEntryDetails
{
[JsonProperty(PropertyName = "totalAmount"), JsonRequired]
public decimal TotalAmount { get; set; }
[JsonProperty(PropertyName = "unitCount"), JsonRequired]
public decimal UnitCount { get; set; }
public List<decimal> DiscountsInUnits { get; private set; }
[JsonProperty(PropertyName = "discountsInUnits"), JsonRequired]
public List<decimal> DiscountsInUnits { get; set; }
public CostEntryDetails()
{
@@ -331,9 +394,16 @@ namespace UCalc.Data
public class CostEntry
{
[JsonProperty(PropertyName = "startDate"), JsonRequired]
public DateTime StartDate { get; set; }
[JsonProperty(PropertyName = "endDate"), JsonRequired]
public DateTime EndDate { get; set; }
[JsonProperty(PropertyName = "amount"), JsonRequired]
public decimal Amount { get; set; }
[JsonProperty(PropertyName = "details"), JsonRequired]
public CostEntryDetails Details { get; set; }
public CostEntry()
@@ -393,12 +463,25 @@ namespace UCalc.Data
public class Cost
{
[JsonProperty(PropertyName = "name"), JsonRequired, JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string Name { get; set; }
[JsonProperty(PropertyName = "division"), JsonRequired, JsonConverter(typeof(StringEnumConverter))]
public CostDivision Division { get; set; }
[JsonProperty(PropertyName = "affectsAll"), JsonRequired]
public bool AffectsAll { get; set; }
[JsonProperty(PropertyName = "includeUnrented"), JsonRequired]
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 Cost()
@@ -439,12 +522,23 @@ namespace UCalc.Data
public class Billing
{
[JsonProperty(PropertyName = "startDate"), JsonRequired]
public DateTime StartDate { get; set; }
[JsonProperty(PropertyName = "endDate"), JsonRequired]
public DateTime EndDate { get; set; }
public Landlord Landlord { get; private set; }
public House House { get; private set; }
public List<Tenant> Tenants { get; private set; }
public List<Cost> Costs { get; private set; }
[JsonProperty(PropertyName = "landlord"), JsonRequired]
public Landlord Landlord { get; 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()
{
+114 -3
View File
@@ -1,18 +1,129 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
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
{
[SuppressMessage("ReSharper", "PossibleNullReferenceException")]
public Billing Load(string path)
{
// TODO
return new Billing();
// TODO: Support older format
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)
{
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.Controls;
using System.Windows.Controls.Primitives;
@@ -78,9 +77,10 @@ namespace UCalc
new BillingWindow(path, billing).Show();
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);
}
// 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.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using UCalc.Annotations;
using UCalc.Data;
@@ -48,7 +49,7 @@ namespace UCalc.Models
return;
}
if (_validated.Add(property))
if (_validated.Add(property) && !_model._loading)
{
property.Validate();
}
@@ -106,6 +107,18 @@ namespace UCalc.Models
return -1;
}
var d1 = prop1.DepthInTree();
var d2 = prop2.DepthInTree();
if (d1 < d2)
{
return 1;
}
if (d1 > d2)
{
return -1;
}
return 0;
});
@@ -127,6 +140,7 @@ namespace UCalc.Models
}
private readonly Validator _validator;
private readonly bool _loading;
public DateTime StartDate { get; }
public DateTime EndDate { get; }
public BillingProperty Root { get; }
@@ -137,8 +151,12 @@ namespace UCalc.Models
StartDate = billing.StartDate;
EndDate = billing.EndDate;
using var validator = BeginValidation();
_loading = true;
Root = new BillingProperty(this, null, billing);
_loading = false;
using var validator = BeginValidation();
validator.Validate(Root);
}
public Validator BeginValidation()
@@ -149,7 +167,83 @@ namespace UCalc.Models
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()
+15 -1
View File
@@ -67,11 +67,25 @@ namespace UCalc.Models
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
{
protected string Name;
protected readonly string Name;
private T _value;
private readonly List<string> _errors;
+16 -2
View File
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UCalc.Controls;
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 readonly HashSet<FlatProperty> _flatProperties;
@@ -65,7 +66,8 @@ namespace UCalc.Models
_flatProperties.Add(flatToProperty?[flat] ?? throw new InvalidOperationException());
}
Validate();
using var validator = Model.BeginValidation();
validator.Validate(this);
}
public override IReadOnlyList<string> Errors => _errors;
@@ -175,6 +177,18 @@ namespace UCalc.Models
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
-2
View File
@@ -13,8 +13,6 @@
PreviewMouseUp="OnPreviewMouseUp"
Icon="logo.ico">
<!-- TODO: Wrap in scroll to handle small screens! -->
<StackPanel Margin="12">
<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();
}
public void OnPrintClick(object sender, RoutedEventArgs e)
private void OnPrintClick(object sender, RoutedEventArgs e)
{
ParentWindow.Print();
}
public void OnAboutClick(object sender, RoutedEventArgs e)
private void OnAboutClick(object sender, RoutedEventArgs e)
{
throw new System.NotImplementedException();
}
+4
View File
@@ -95,4 +95,8 @@
<Resource Include="logo.ico"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3"/>
</ItemGroup>
</Project>