Implemented cost entries excluding cost entry details.
This commit is contained in:
@@ -16,5 +16,8 @@ namespace UCalc
|
|||||||
|
|
||||||
public static readonly ImmutableList<string> SalutationStrs =
|
public static readonly ImmutableList<string> SalutationStrs =
|
||||||
((Salutation[]) Enum.GetValues(typeof(Salutation))).Select(value => value.AsString()).ToImmutableList();
|
((Salutation[]) Enum.GetValues(typeof(Salutation))).Select(value => value.AsString()).ToImmutableList();
|
||||||
|
|
||||||
|
public static readonly ImmutableList<string> CostDivisionStrs =
|
||||||
|
((CostDivision[]) Enum.GetValues(typeof(CostDivision))).Select(value => value.AsString()).ToImmutableList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,6 +79,21 @@ namespace UCalc.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class FlatToAffectedConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public CostProperty Cost { get; set; }
|
||||||
|
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
return Cost.AffectedFlats.Contains((FlatProperty) value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class EmptyMultiPropertyToVisibilityConverter : IValueConverter
|
public class EmptyMultiPropertyToVisibilityConverter : IValueConverter
|
||||||
{
|
{
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
@@ -133,4 +148,17 @@ namespace UCalc.Controls
|
|||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class NegateConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
return !(bool?) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
return !(bool?) value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+207
-3
@@ -14,7 +14,14 @@
|
|||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
Icon="logo.ico">
|
Icon="logo.ico">
|
||||||
|
|
||||||
|
<Window.Resources>
|
||||||
|
<controls:NegateConverter x:Key="NegateConverter" />
|
||||||
|
<controls:FlatToAffectedConverter x:Key="FlatToAffectedConverter" />
|
||||||
|
<controls:DatePickerTextToDateTimeConverter x:Key="DatePickerTextToDateTimeConverter" />
|
||||||
|
<controls:EmptyMultiPropertyToVisibilityConverter x:Key="EmptyMultiPropertyToVisibilityConverter" />
|
||||||
|
</Window.Resources>
|
||||||
|
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<StackPanel DockPanel.Dock="Bottom">
|
<StackPanel DockPanel.Dock="Bottom">
|
||||||
<Separator Margin="0, 12, 0, 0"
|
<Separator Margin="0, 12, 0, 0"
|
||||||
@@ -52,9 +59,206 @@
|
|||||||
Text="{Binding Path=Cost.Name.Value, RelativeSource={RelativeSource AncestorType=local:CostWindow}, UpdateSourceTrigger=PropertyChanged}" />
|
Text="{Binding Path=Cost.Name.Value, RelativeSource={RelativeSource AncestorType=local:CostWindow}, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
|
||||||
|
<DockPanel Margin="12, 8, 12, 0">
|
||||||
|
<TextBlock DockPanel.Dock="Left"
|
||||||
|
Margin="4, 0, 4, 0"
|
||||||
|
Text="Detailiert in Ausdruck berechnen:"
|
||||||
|
Width="172"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Foreground="{x:Static local:Constants.SubMainColor}" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
IsChecked="{Binding Path=Cost.DisplayInBill.Value, RelativeSource={RelativeSource AncestorType=local:CostWindow}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
|
||||||
|
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<controls:SectionHeader Header="Verteilung" />
|
||||||
|
|
||||||
|
<DockPanel Margin="12, 0, 12, 0">
|
||||||
|
<Label DockPanel.Dock="Left"
|
||||||
|
Content="Betrifft alle Wohnungen:"
|
||||||
|
Width="180"
|
||||||
|
Foreground="{x:Static local:Constants.SubMainColor}" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
IsChecked="{Binding Path=Cost.AffectsAll.Value, RelativeSource={RelativeSource AncestorType=local:CostWindow}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
|
||||||
|
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<DockPanel Margin="12, 8, 12, 0">
|
||||||
|
<Label DockPanel.Dock="Left"
|
||||||
|
Content="Betroffene Wohnungen:"
|
||||||
|
Width="180"
|
||||||
|
Foreground="{x:Static local:Constants.SubMainColor}" />
|
||||||
|
|
||||||
|
<controls:ErrorIcon Margin="12, 0, 0, 0"
|
||||||
|
DockPanel.Dock="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Property="{Binding Path=Cost.AffectedFlats, RelativeSource={RelativeSource AncestorType=local:CostWindow}}" />
|
||||||
|
|
||||||
|
<ListBox Height="120"
|
||||||
|
ItemsSource="{Binding Path=House.Flats, RelativeSource={RelativeSource AncestorType=local:CostWindow}}"
|
||||||
|
IsEnabled="{Binding Path=Cost.AffectsAll.Value, RelativeSource={RelativeSource AncestorType=local:CostWindow}, Converter={StaticResource NegateConverter}}">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<CheckBox Content="{Binding Name.Value}"
|
||||||
|
IsChecked="{Binding Path=., Converter={StaticResource FlatToAffectedConverter}, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
Checked="OnFlatChecked"
|
||||||
|
Unchecked="OnFlatUnchecked" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<DockPanel Margin="12, 12, 12, 0">
|
||||||
|
<TextBlock DockPanel.Dock="Left"
|
||||||
|
Margin="4, 0, 4, 0"
|
||||||
|
Text="Lege Kosten für unvermietete Wohnungen auf vermietete Wohnungen um:"
|
||||||
|
Width="172"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Foreground="{x:Static local:Constants.SubMainColor}" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
IsChecked="{Binding Path=Cost.IncludeUnrented.Value, RelativeSource={RelativeSource AncestorType=local:CostWindow}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
|
||||||
|
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<DockPanel Margin="12, 8, 12, 8">
|
||||||
|
<Label DockPanel.Dock="Left"
|
||||||
|
Content="Art der Aufteilung:"
|
||||||
|
Width="180"
|
||||||
|
Foreground="{x:Static local:Constants.SubMainColor}" />
|
||||||
|
|
||||||
|
<ComboBox Margin="0, 0, 28, 0"
|
||||||
|
ItemsSource="{x:Static local:Constants.CostDivisionStrs}"
|
||||||
|
SelectedIndex="{Binding Path=Cost.Division.Value, RelativeSource={RelativeSource AncestorType=local:CostWindow}}" />
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<controls:SectionHeader Header="Zeiträume" />
|
||||||
|
|
||||||
|
<DockPanel>
|
||||||
|
<controls:ErrorIcon Margin="0, 0, 12, 0"
|
||||||
|
DockPanel.Dock="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Property="{Binding Path=Cost.Entries, RelativeSource={RelativeSource AncestorType=local:CostWindow}}"
|
||||||
|
Visibility="{Binding Path=Cost.Entries.Count, RelativeSource={RelativeSource AncestorType=local:CostWindow}, Converter={StaticResource EmptyMultiPropertyToVisibilityConverter}}" />
|
||||||
|
|
||||||
|
<controls:HighlightButton Margin="12, 0, 12, 0"
|
||||||
|
HighlightForeground="{x:Static local:Constants.SubMainColor}"
|
||||||
|
HighlightBackground="{x:Static local:Constants.MainColor}"
|
||||||
|
Click="OnAddCostEntryClick">
|
||||||
|
<Viewbox Width="24"
|
||||||
|
Height="24"
|
||||||
|
Margin="12, 12, 6, 12"
|
||||||
|
Stretch="Uniform">
|
||||||
|
<Canvas Width="512" Height="512">
|
||||||
|
<Canvas.RenderTransform>
|
||||||
|
<TranslateTransform X="0" Y="0" />
|
||||||
|
</Canvas.RenderTransform>
|
||||||
|
<Path>
|
||||||
|
<Path.Data>
|
||||||
|
<PathGeometry
|
||||||
|
Figures="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"
|
||||||
|
FillRule="NonZero" />
|
||||||
|
</Path.Data>
|
||||||
|
</Path>
|
||||||
|
</Canvas>
|
||||||
|
</Viewbox>
|
||||||
|
|
||||||
|
<Label Content="Zeitraum hinzufügen"
|
||||||
|
Margin="0, 12, 12, 12" />
|
||||||
|
</controls:HighlightButton>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<ItemsControl
|
||||||
|
ItemsSource="{Binding Path=Cost.Entries, RelativeSource={RelativeSource AncestorType=local:CostWindow}}"
|
||||||
|
Margin="12, 0, 12, 12">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<DockPanel>
|
||||||
|
<controls:HighlightButton DockPanel.Dock="Right"
|
||||||
|
HighlightForeground="Red"
|
||||||
|
HighlightBackground="White"
|
||||||
|
Click="OnCostEntryDeleteClick">
|
||||||
|
<Viewbox Width="24"
|
||||||
|
Height="24"
|
||||||
|
Margin="8, 12, 8, 12"
|
||||||
|
Stretch="Uniform">
|
||||||
|
<Canvas Width="448" Height="512">
|
||||||
|
<Canvas.RenderTransform>
|
||||||
|
<TranslateTransform X="0" Y="0" />
|
||||||
|
</Canvas.RenderTransform>
|
||||||
|
<Path>
|
||||||
|
<Path.Data>
|
||||||
|
<PathGeometry
|
||||||
|
Figures="M32 464a48 48 0 0 0 48 48h288a48 48 0 0 0 48-48V128H32zm272-256a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0zm-96 0a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0zm-96 0a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0zM432 32H312l-9.4-18.7A24 24 0 0 0 281.1 0H166.8a23.72 23.72 0 0 0-21.4 13.3L136 32H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16z"
|
||||||
|
FillRule="NonZero" />
|
||||||
|
</Path.Data>
|
||||||
|
</Path>
|
||||||
|
</Canvas>
|
||||||
|
</Viewbox>
|
||||||
|
</controls:HighlightButton>
|
||||||
|
|
||||||
|
<StackPanel Margin="0, 8, 8, 8">
|
||||||
|
<DockPanel Margin="0, 0, 0, 2">
|
||||||
|
<Label DockPanel.Dock="Left" Content="Startdatum:"
|
||||||
|
Width="180"
|
||||||
|
Foreground="{x:Static local:Constants.SubMainColor}" />
|
||||||
|
|
||||||
|
<controls:ErrorIcon Margin="12, 0, 0, 0"
|
||||||
|
DockPanel.Dock="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Property="{Binding StartDate}" />
|
||||||
|
|
||||||
|
<DatePicker VerticalAlignment="Center"
|
||||||
|
MinHeight="22"
|
||||||
|
DisplayDateStart="{Binding Path=Model.StartDate, RelativeSource={RelativeSource AncestorType=local:CostWindow}, Mode=OneWay}"
|
||||||
|
DisplayDateEnd="{Binding Path=Model.EndDate, RelativeSource={RelativeSource AncestorType=local:CostWindow}, Mode=OneWay}"
|
||||||
|
Text="{Binding Path=StartDate.Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Converter={StaticResource DatePickerTextToDateTimeConverter}}" />
|
||||||
|
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<DockPanel Margin="0, 2, 0, 2">
|
||||||
|
<Label DockPanel.Dock="Left" Content="Enddatum:"
|
||||||
|
Width="180"
|
||||||
|
Foreground="{x:Static local:Constants.SubMainColor}" />
|
||||||
|
|
||||||
|
<controls:ErrorIcon Margin="12, 0, 0, 0"
|
||||||
|
DockPanel.Dock="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Property="{Binding EndDate}" />
|
||||||
|
|
||||||
|
<DatePicker VerticalAlignment="Center"
|
||||||
|
MinHeight="22"
|
||||||
|
DisplayDateStart="{Binding Path=Model.StartDate, RelativeSource={RelativeSource AncestorType=local:CostWindow}, Mode=OneWay}"
|
||||||
|
DisplayDateEnd="{Binding Path=Model.EndDate, RelativeSource={RelativeSource AncestorType=local:CostWindow}, Mode=OneWay}"
|
||||||
|
Text="{Binding Path=EndDate.Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Converter={StaticResource DatePickerTextToDateTimeConverter}}" />
|
||||||
|
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<DockPanel Margin="0, 2, 0, 0">
|
||||||
|
<Label DockPanel.Dock="Left" Content="Betrag:"
|
||||||
|
Width="180"
|
||||||
|
Foreground="{x:Static local:Constants.SubMainColor}" />
|
||||||
|
|
||||||
|
<controls:ErrorIcon Margin="12, 0, 0, 0"
|
||||||
|
DockPanel.Dock="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Property="{Binding Amount}" />
|
||||||
|
|
||||||
|
<TextBox VerticalAlignment="Center"
|
||||||
|
MinHeight="22"
|
||||||
|
Text="{Binding Path=Amount.Value, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</DockPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
|
||||||
</Window>
|
</Window>
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using UCalc.Controls;
|
||||||
using UCalc.Models;
|
using UCalc.Models;
|
||||||
|
|
||||||
namespace UCalc
|
namespace UCalc
|
||||||
@@ -14,13 +16,40 @@ namespace UCalc
|
|||||||
Model = model;
|
Model = model;
|
||||||
Cost = cost;
|
Cost = cost;
|
||||||
House = house;
|
House = house;
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
((FlatToAffectedConverter) FindResource("FlatToAffectedConverter")).Cost = Cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnOkClick(object sender, RoutedEventArgs e)
|
private void OnOkClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnFlatChecked(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var flat = (FlatProperty) ((CheckBox) sender).DataContext;
|
||||||
|
|
||||||
|
Cost.AffectedFlats.Add(flat);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFlatUnchecked(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var flat = (FlatProperty) ((CheckBox) sender).DataContext;
|
||||||
|
|
||||||
|
Cost.AffectedFlats.Remove(flat);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCostEntryDeleteClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var entry = (CostEntryProperty) ((HighlightButton) sender).DataContext;
|
||||||
|
|
||||||
|
Cost.Entries.Remove(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAddCostEntryClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Cost.Entries.Add();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+24
-6
@@ -296,7 +296,7 @@ namespace UCalc.Data
|
|||||||
|
|
||||||
public class CostEntryDetails
|
public class CostEntryDetails
|
||||||
{
|
{
|
||||||
public decimal TotalPrice { get; set; }
|
public decimal TotalAmount { get; set; }
|
||||||
public decimal UnitCount { get; set; }
|
public decimal UnitCount { get; set; }
|
||||||
public List<decimal> DiscountsInUnits { get; private set; }
|
public List<decimal> DiscountsInUnits { get; private set; }
|
||||||
|
|
||||||
@@ -307,7 +307,7 @@ namespace UCalc.Data
|
|||||||
|
|
||||||
private bool Equals(CostEntryDetails other)
|
private bool Equals(CostEntryDetails other)
|
||||||
{
|
{
|
||||||
return TotalPrice == other.TotalPrice && UnitCount == other.UnitCount &&
|
return TotalAmount == other.TotalAmount && UnitCount == other.UnitCount &&
|
||||||
DiscountsInUnits.SequenceEqual(other.DiscountsInUnits);
|
DiscountsInUnits.SequenceEqual(other.DiscountsInUnits);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,7 +322,7 @@ namespace UCalc.Data
|
|||||||
{
|
{
|
||||||
return new CostEntryDetails
|
return new CostEntryDetails
|
||||||
{
|
{
|
||||||
TotalPrice = TotalPrice,
|
TotalAmount = TotalAmount,
|
||||||
UnitCount = UnitCount,
|
UnitCount = UnitCount,
|
||||||
DiscountsInUnits = new List<decimal>(DiscountsInUnits)
|
DiscountsInUnits = new List<decimal>(DiscountsInUnits)
|
||||||
};
|
};
|
||||||
@@ -333,7 +333,7 @@ namespace UCalc.Data
|
|||||||
{
|
{
|
||||||
public DateTime StartDate { get; set; }
|
public DateTime StartDate { get; set; }
|
||||||
public DateTime EndDate { get; set; }
|
public DateTime EndDate { get; set; }
|
||||||
public decimal Price { get; set; }
|
public decimal Amount { get; set; }
|
||||||
public CostEntryDetails Details { get; set; }
|
public CostEntryDetails Details { get; set; }
|
||||||
|
|
||||||
public CostEntry()
|
public CostEntry()
|
||||||
@@ -343,7 +343,7 @@ namespace UCalc.Data
|
|||||||
|
|
||||||
private bool Equals(CostEntry other)
|
private bool Equals(CostEntry other)
|
||||||
{
|
{
|
||||||
return StartDate.Equals(other.StartDate) && EndDate.Equals(other.EndDate) && Price == other.Price &&
|
return StartDate.Equals(other.StartDate) && EndDate.Equals(other.EndDate) && Amount == other.Amount &&
|
||||||
Details.Equals(other.Details);
|
Details.Equals(other.Details);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,7 +360,7 @@ namespace UCalc.Data
|
|||||||
{
|
{
|
||||||
StartDate = StartDate,
|
StartDate = StartDate,
|
||||||
EndDate = EndDate,
|
EndDate = EndDate,
|
||||||
Price = Price,
|
Amount = Amount,
|
||||||
Details = Details?.Clone()
|
Details = Details?.Clone()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -373,6 +373,24 @@ namespace UCalc.Data
|
|||||||
Size
|
Size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class CostDivisions
|
||||||
|
{
|
||||||
|
public static string AsString(this CostDivision division)
|
||||||
|
{
|
||||||
|
switch (division)
|
||||||
|
{
|
||||||
|
case CostDivision.Person:
|
||||||
|
return "Pro Person";
|
||||||
|
case CostDivision.Flat:
|
||||||
|
return "Pro Wohnung";
|
||||||
|
case CostDivision.Size:
|
||||||
|
return "Pro m²";
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class Cost
|
public class Cost
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UCalc.Data;
|
||||||
|
|
||||||
|
namespace UCalc.Models
|
||||||
|
{
|
||||||
|
public class CostEntriesProperty : MultiProperty<CostEntryProperty>
|
||||||
|
{
|
||||||
|
public CostEntriesProperty(Model model, Property parent, IEnumerable<CostEntry> data) : base(model, parent,
|
||||||
|
"Zeiträume: Geben Sie einen oder mehr Zeiträume an.")
|
||||||
|
{
|
||||||
|
foreach (var entry in data)
|
||||||
|
{
|
||||||
|
Add(new CostEntryProperty(model, this, entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add()
|
||||||
|
{
|
||||||
|
var entry = new CostEntryProperty(Model, this, new CostEntry());
|
||||||
|
entry.StartDate.Value = null;
|
||||||
|
entry.EndDate.Value = null;
|
||||||
|
base.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void Remove(CostEntryProperty entry)
|
||||||
|
{
|
||||||
|
using var validator = Model.BeginValidation();
|
||||||
|
|
||||||
|
base.Remove(entry);
|
||||||
|
|
||||||
|
validator.ValidateRange(Properties.Select(otherEntry => otherEntry.EndDate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using UCalc.Controls;
|
||||||
|
using UCalc.Data;
|
||||||
|
|
||||||
|
namespace UCalc.Models
|
||||||
|
{
|
||||||
|
public class DateProperty : ValueProperty<DateTime?>
|
||||||
|
{
|
||||||
|
public DateProperty(Model model, Property parent, string name, DateTime value) : base(model, parent, name,
|
||||||
|
value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string ValidateValue()
|
||||||
|
{
|
||||||
|
var error = Value == null ? $"{Name}: Geben Sie einen Wert ein." : "";
|
||||||
|
|
||||||
|
using var validator = Model.BeginValidation();
|
||||||
|
validator.Validate(((CostEntryProperty) Parent).EndDate);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EndDateProperty : ValueProperty<DateTime?>
|
||||||
|
{
|
||||||
|
public EndDateProperty(Model model, Property parent, string name, DateTime value) : base(model, parent, name,
|
||||||
|
value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string ValidateValue()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Value == null)
|
||||||
|
{
|
||||||
|
return $"{Name}: Geben Sie einen Wert ein.";
|
||||||
|
}
|
||||||
|
|
||||||
|
var startDate = ((CostEntryProperty) Parent).StartDate.Value;
|
||||||
|
if (startDate != null)
|
||||||
|
{
|
||||||
|
if (startDate > Value)
|
||||||
|
{
|
||||||
|
return $"{Name}: Das Enddatum liegt vor dem Startdatum.";
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var entry in ((CostProperty) Parent.Parent.Parent).Entries)
|
||||||
|
{
|
||||||
|
if (!ReferenceEquals(entry.EndDate, this) && entry.StartDate.Value != null &&
|
||||||
|
entry.EndDate.Value != null &&
|
||||||
|
startDate.Value.Intersects(Value.Value, entry.StartDate.Value.Value,
|
||||||
|
entry.EndDate.Value.Value))
|
||||||
|
{
|
||||||
|
return $"{Name}: Dieser Zeitraum überschneidet sich mit einem anderen.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
using var validator = Model.BeginValidation();
|
||||||
|
validator.ValidateRange(((CostProperty) Parent.Parent.Parent).Entries.Select(entry => entry.EndDate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CostEntryProperty : NestedProperty
|
||||||
|
{
|
||||||
|
public DateProperty StartDate { get; }
|
||||||
|
public EndDateProperty EndDate { get; }
|
||||||
|
public PositiveDecimalProperty Amount { get; }
|
||||||
|
|
||||||
|
// TODO: Details
|
||||||
|
|
||||||
|
public CostEntryProperty(Model model, Property parent, CostEntry data) : base(model, parent)
|
||||||
|
{
|
||||||
|
StartDate = Add(new DateProperty(model, this, "Startdatum", data.StartDate));
|
||||||
|
EndDate = Add(new EndDateProperty(model, this, "Enddatum", data.EndDate));
|
||||||
|
Amount = Add(new PositiveDecimalProperty(model, this, "Betrag", data.Amount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,97 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using UCalc.Data;
|
using UCalc.Data;
|
||||||
|
|
||||||
namespace UCalc.Models
|
namespace UCalc.Models
|
||||||
{
|
{
|
||||||
|
public class AffectsAllProperty : AlwaysValidProperty<bool>
|
||||||
|
{
|
||||||
|
public AffectsAllProperty(Model model, Property parent, string name, bool value) : base(model, parent, name,
|
||||||
|
value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string ValidateValue()
|
||||||
|
{
|
||||||
|
using var validator = Model.BeginValidation();
|
||||||
|
validator.Validate(((CostProperty) Parent).AffectedFlats);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AffectedFlatsProperty : Property
|
||||||
|
{
|
||||||
|
private const string NoFlatsError = "Betroffene Wohnungen: Es wurde keine Wohnung zugewiesen.";
|
||||||
|
private readonly HashSet<FlatProperty> _flats;
|
||||||
|
private readonly List<string> _errors;
|
||||||
|
|
||||||
|
public AffectedFlatsProperty(Model model, Property parent, IEnumerable<Flat> data,
|
||||||
|
IReadOnlyDictionary<Flat, FlatProperty> flatToProperty = null) : base(model, parent)
|
||||||
|
{
|
||||||
|
_flats = new HashSet<FlatProperty>();
|
||||||
|
_errors = new List<string>();
|
||||||
|
|
||||||
|
foreach (var flat in data)
|
||||||
|
{
|
||||||
|
_flats.Add(flatToProperty?[flat] ?? throw new InvalidOperationException());
|
||||||
|
}
|
||||||
|
|
||||||
|
Validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IReadOnlyList<string> Errors => _errors;
|
||||||
|
|
||||||
|
public void Add(FlatProperty flat)
|
||||||
|
{
|
||||||
|
if (!_flats.Add(flat))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var validator = Model.BeginValidation();
|
||||||
|
validator.Validate(this);
|
||||||
|
Modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(FlatProperty flat)
|
||||||
|
{
|
||||||
|
if (!_flats.Remove(flat))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var validator = Model.BeginValidation();
|
||||||
|
validator.Validate(this);
|
||||||
|
Modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(FlatProperty flat)
|
||||||
|
{
|
||||||
|
return _flats.Contains(flat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed override void Validate()
|
||||||
|
{
|
||||||
|
_errors.Clear();
|
||||||
|
|
||||||
|
if (_flats.Count == 0 && !((CostProperty) Parent).AffectsAll.Value)
|
||||||
|
{
|
||||||
|
_errors.Add(NoFlatsError);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var validator = Model.BeginValidation();
|
||||||
|
validator.Notify(this, "Errors");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class CostProperty : NestedProperty
|
public class CostProperty : NestedProperty
|
||||||
{
|
{
|
||||||
public NotEmptyStringProperty Name { get; }
|
public NotEmptyStringProperty Name { get; }
|
||||||
public AlwaysValidProperty<int> Division { get; }
|
public AlwaysValidProperty<int> Division { get; }
|
||||||
public AlwaysValidProperty<bool> AffectsAll { get; }
|
public AffectsAllProperty AffectsAll { get; }
|
||||||
|
|
||||||
public AlwaysValidProperty<bool> IncludeUnrented { get; }
|
public AlwaysValidProperty<bool> IncludeUnrented { get; }
|
||||||
|
public AffectedFlatsProperty AffectedFlats { get; }
|
||||||
// TODO: public HashSet<Flat> AffectedFlats { get; }
|
public CostEntriesProperty Entries { get; }
|
||||||
// TODO: public List<CostEntry> Entries { get; }
|
|
||||||
public AlwaysValidProperty<bool> DisplayInBill { get; }
|
public AlwaysValidProperty<bool> DisplayInBill { get; }
|
||||||
|
|
||||||
public CostProperty(Model model, Property parent, Cost cost,
|
public CostProperty(Model model, Property parent, Cost cost,
|
||||||
@@ -20,9 +99,11 @@ namespace UCalc.Models
|
|||||||
{
|
{
|
||||||
Name = Add(new NotEmptyStringProperty(model, this, "Name", cost.Name));
|
Name = Add(new NotEmptyStringProperty(model, this, "Name", cost.Name));
|
||||||
Division = Add(new AlwaysValidProperty<int>(model, this, "Aufteilung", (int) cost.Division));
|
Division = Add(new AlwaysValidProperty<int>(model, this, "Aufteilung", (int) cost.Division));
|
||||||
AffectsAll = Add(new AlwaysValidProperty<bool>(model, this, "Betrifft alle", cost.AffectsAll));
|
AffectsAll = Add(new AffectsAllProperty(model, this, "Betrifft alle", cost.AffectsAll));
|
||||||
|
AffectedFlats = Add(new AffectedFlatsProperty(model, this, cost.AffectedFlats, flatToProperty));
|
||||||
IncludeUnrented =
|
IncludeUnrented =
|
||||||
Add(new AlwaysValidProperty<bool>(model, this, "Unvermietete einbeziehen", cost.IncludeUnrented));
|
Add(new AlwaysValidProperty<bool>(model, this, "Unvermietete einbeziehen", cost.IncludeUnrented));
|
||||||
|
Entries = Add(new CostEntriesProperty(model, this, cost.Entries));
|
||||||
DisplayInBill = Add(new AlwaysValidProperty<bool>(model, this, "In Rechnung anzeigen", cost.DisplayInBill));
|
DisplayInBill = Add(new AlwaysValidProperty<bool>(model, this, "In Rechnung anzeigen", cost.DisplayInBill));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,56 +103,61 @@ namespace UCalc.Models
|
|||||||
{
|
{
|
||||||
_errors.Clear();
|
_errors.Clear();
|
||||||
|
|
||||||
if (_flatProperties.Count == 0)
|
try
|
||||||
{
|
{
|
||||||
_errors.Add(NoFlatsError);
|
if (_flatProperties.Count == 0)
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var entryDate = ((TenantProperty) Parent).EntryDate.Value;
|
|
||||||
var departureDate = ((TenantProperty) Parent).DepartureDate.Value;
|
|
||||||
|
|
||||||
foreach (var flatProperty in _flatProperties)
|
|
||||||
{
|
|
||||||
foreach (var tenant in Model.Root.Tenants)
|
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(this, tenant.RentedFlats))
|
_errors.Add(NoFlatsError);
|
||||||
{
|
return;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (tenant.RentedFlats.IsRented(flatProperty, entryDate, departureDate))
|
var entryDate = ((TenantProperty) Parent).EntryDate.Value;
|
||||||
{
|
var departureDate = ((TenantProperty) Parent).DepartureDate.Value;
|
||||||
var error = $"Gemietete Wohnungen: Die Wohnung \"{flatProperty.Name.Value}\" ist bereits ";
|
|
||||||
|
|
||||||
if (entryDate != null)
|
foreach (var flatProperty in _flatProperties)
|
||||||
|
{
|
||||||
|
foreach (var tenant in Model.Root.Tenants)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(this, tenant.RentedFlats))
|
||||||
{
|
{
|
||||||
if (departureDate != null)
|
continue;
|
||||||
{
|
|
||||||
_errors.Add(
|
|
||||||
$"von {entryDate.Value.ToString(Constants.DateFormat)} - {departureDate.Value.ToString(Constants.DateFormat)} ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_errors.Add($"seit dem {entryDate.Value.ToString(Constants.DateFormat)} ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (departureDate != null)
|
|
||||||
{
|
|
||||||
_errors.Add($"bis zum {departureDate.Value.ToString(Constants.DateFormat)} ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error += "an einen anderen Mieter vermietet.";
|
if (tenant.RentedFlats.IsRented(flatProperty, entryDate, departureDate))
|
||||||
_errors.Add(error);
|
{
|
||||||
break;
|
var error = $"Gemietete Wohnungen: Die Wohnung \"{flatProperty.Name.Value}\" ist bereits ";
|
||||||
|
|
||||||
|
if (entryDate != null)
|
||||||
|
{
|
||||||
|
if (departureDate != null)
|
||||||
|
{
|
||||||
|
_errors.Add(
|
||||||
|
$"von {entryDate.Value.ToString(Constants.DateFormat)} - {departureDate.Value.ToString(Constants.DateFormat)} ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_errors.Add($"seit dem {entryDate.Value.ToString(Constants.DateFormat)} ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (departureDate != null)
|
||||||
|
{
|
||||||
|
_errors.Add($"bis zum {departureDate.Value.ToString(Constants.DateFormat)} ");
|
||||||
|
}
|
||||||
|
|
||||||
|
error += "an einen anderen Mieter vermietet.";
|
||||||
|
_errors.Add(error);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
using var validator = Model.BeginValidation();
|
||||||
|
validator.Notify(this, "Errors");
|
||||||
|
|
||||||
using var validator = Model.BeginValidation();
|
validator.ValidateRange(Model.Root.Tenants);
|
||||||
validator.Notify(this, "Errors");
|
}
|
||||||
|
|
||||||
validator.ValidateRange(Model.Root.Tenants);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsRented(FlatProperty flat, DateTime? start, DateTime? end)
|
private bool IsRented(FlatProperty flat, DateTime? start, DateTime? end)
|
||||||
|
|||||||
Reference in New Issue
Block a user