-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Copyright 2021 Valters Melnalksnis | ||
// Licensed under the GNU Affero General Public License v3.0 or later. | ||
// See LICENSE.txt file in the project root for full license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
|
||
using Gnomeshade.WebApi.Models.Transactions; | ||
|
||
namespace Gnomeshade.Avalonia.Core.Transactions.Loans.Migration; | ||
|
||
/// <inheritdoc /> | ||
internal sealed class LoanCounterpartyComparer : IEqualityComparer<Loan?> | ||
{ | ||
public bool Equals(Loan? x, Loan? y) | ||
{ | ||
if (ReferenceEquals(x, y)) | ||
{ | ||
return true; | ||
Check warning on line 19 in source/Gnomeshade.Avalonia.Core/Transactions/Loans/Migration/LoanCounterpartyComparer.cs Codecov / codecov/patchsource/Gnomeshade.Avalonia.Core/Transactions/Loans/Migration/LoanCounterpartyComparer.cs#L19
|
||
} | ||
|
||
if (ReferenceEquals(x, null) || ReferenceEquals(y, null)) | ||
{ | ||
return false; | ||
Check warning on line 24 in source/Gnomeshade.Avalonia.Core/Transactions/Loans/Migration/LoanCounterpartyComparer.cs Codecov / codecov/patchsource/Gnomeshade.Avalonia.Core/Transactions/Loans/Migration/LoanCounterpartyComparer.cs#L24
|
||
} | ||
|
||
return | ||
(x.IssuingCounterpartyId == y.IssuingCounterpartyId && x.ReceivingCounterpartyId == y.ReceivingCounterpartyId) || | ||
(x.IssuingCounterpartyId == y.ReceivingCounterpartyId && x.ReceivingCounterpartyId == y.IssuingCounterpartyId); | ||
} | ||
|
||
public int GetHashCode(Loan obj) | ||
{ | ||
return HashCode.Combine(obj.IssuingCounterpartyId, obj.ReceivingCounterpartyId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Copyright 2021 Valters Melnalksnis | ||
// Licensed under the GNU Affero General Public License v3.0 or later. | ||
// See LICENSE.txt file in the project root for full license information. | ||
|
||
using System.Collections.Generic; | ||
|
||
namespace Gnomeshade.Avalonia.Core.Transactions.Loans.Migration; | ||
|
||
/// <summary>Row for displaying how a loan will look after migrating.</summary> | ||
public sealed class LoanMigrationRow : PropertyChangedBase | ||
{ | ||
/// <summary>Initializes a new instance of the <see cref="LoanMigrationRow"/> class.</summary> | ||
/// <param name="name">The name of the loan.</param> | ||
/// <param name="issuer">The issuer of the loan.</param> | ||
/// <param name="receiver">The receiver of the loan.</param> | ||
/// <param name="amounts">The amount of the loan payments.</param> | ||
/// <param name="currency">The currency code of <paramref name="amounts"/>.</param> | ||
public LoanMigrationRow(string name, string issuer, string receiver, List<decimal> amounts, string currency) | ||
{ | ||
Name = name; | ||
Issuer = issuer; | ||
Receiver = receiver; | ||
Amounts = amounts; | ||
Currency = currency; | ||
} | ||
|
||
/// <summary>Gets the name of the loan.</summary> | ||
public string Name { get; } | ||
|
||
/// <summary>Gets the issuer of the loan.</summary> | ||
public string Issuer { get; } | ||
|
||
/// <summary>Gets the receiver of the loan.</summary> | ||
public string Receiver { get; } | ||
|
||
/// <summary>Gets the loan payment amounts.</summary> | ||
public List<decimal> Amounts { get; } | ||
|
||
/// <summary>Gets the currency code of <see cref="Amounts"/>.</summary> | ||
public string Currency { get; } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// Copyright 2021 Valters Melnalksnis | ||
// Licensed under the GNU Affero General Public License v3.0 or later. | ||
// See LICENSE.txt file in the project root for full license information. | ||
|
||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
|
||
using Gnomeshade.WebApi.Client; | ||
|
||
using NodaTime; | ||
|
||
using PropertyChanged.SourceGenerator; | ||
|
||
namespace Gnomeshade.Avalonia.Core.Transactions.Loans.Migration; | ||
|
||
/// <summary>Overview of loans to be migrated to named loans/loan payments.</summary> | ||
public sealed partial class LoanMigrationViewModel : ViewModelBase | ||
{ | ||
private readonly IGnomeshadeClient _gnomeshadeClient; | ||
|
||
/// <summary>Gets a collection of all current loans.</summary> | ||
[Notify(Setter.Private)] | ||
private List<LoanOverview> _loans = []; | ||
|
||
/// <summary>Gets a collection of all loan payments that will be created.</summary> | ||
[Notify(Setter.Private)] | ||
private List<LoanMigrationRow> _migratedLoans = []; | ||
|
||
/// <summary>Initializes a new instance of the <see cref="LoanMigrationViewModel"/> class.</summary> | ||
/// <param name="activityService">Service for indicating the activity of the application to the user.</param> | ||
/// <param name="gnomeshadeClient">A strongly typed API client.</param> | ||
public LoanMigrationViewModel(IActivityService activityService, IGnomeshadeClient gnomeshadeClient) | ||
: base(activityService) | ||
{ | ||
_gnomeshadeClient = gnomeshadeClient; | ||
} | ||
|
||
/// <inheritdoc /> | ||
protected override async Task Refresh() | ||
{ | ||
var loans = await _gnomeshadeClient.GetLoansAsync(); | ||
var counterparties = await _gnomeshadeClient.GetCounterpartiesAsync(); | ||
var transactions = await _gnomeshadeClient.GetDetailedTransactionsAsync(new(Instant.MinValue, Instant.MaxValue)); | ||
var currencies = await _gnomeshadeClient.GetCurrenciesAsync(); | ||
|
||
MigratedLoans = loans | ||
.OrderBy(loan => | ||
{ | ||
var transaction = transactions.Single(transaction => transaction.Id == loan.TransactionId); | ||
return transaction.ValuedAt ?? transaction.BookedAt; | ||
}) | ||
.GroupBy(loan => loan, new LoanCounterpartyComparer()) | ||
.Select(grouping => | ||
{ | ||
var counterparty1 = counterparties.Single(counterparty => counterparty.Id == grouping.Key.IssuingCounterpartyId); | ||
var counterparty2 = counterparties.Single(counterparty => counterparty.Id == grouping.Key.ReceivingCounterpartyId); | ||
return (counterparty1, counterparty2, grouping.ToArray()); | ||
}) | ||
.SelectMany(loanTuple => | ||
{ | ||
var issuer = loanTuple.Item3.First().IssuingCounterpartyId == loanTuple.counterparty1.Id | ||
? loanTuple.counterparty1 | ||
: loanTuple.counterparty2; | ||
var receiver = loanTuple.counterparty1 == issuer | ||
? loanTuple.counterparty2 | ||
: loanTuple.counterparty1; | ||
return loanTuple.Item3.GroupBy(loan => loan.CurrencyId).Select(grouping => | ||
{ | ||
var currency = currencies.Single(currency => currency.Id == grouping.Key); | ||
var name = $"{issuer.Name} loan to {receiver.Name} ({currency.AlphabeticCode})"; | ||
return new LoanMigrationRow(name, issuer.Name, receiver.Name, grouping.Select(loan => loan.Amount).ToList(), currency.AlphabeticCode); | ||
}); | ||
}) | ||
.ToList(); | ||
|
||
Loans = transactions | ||
.SelectMany(transaction => transaction.Loans) | ||
.Select(loan => new LoanOverview( | ||
loan.Id, | ||
counterparties.Single(counterparty => counterparty.Id == loan.IssuingCounterpartyId).Name, | ||
counterparties.Single(counterparty => counterparty.Id == loan.ReceivingCounterpartyId).Name, | ||
loan.Amount, | ||
currencies.Single(currency => currency.Id == loan.CurrencyId).AlphabeticCode)) | ||
.ToList(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
<UserControl | ||
xmlns="https://github.com/avaloniaui" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | ||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
xmlns:dd="clr-namespace:Gnomeshade.Avalonia.Core.DesignTime;assembly=Gnomeshade.Avalonia.Core" | ||
xmlns:migration="clr-namespace:Gnomeshade.Avalonia.Core.Transactions.Loans.Migration;assembly=Gnomeshade.Avalonia.Core" | ||
xmlns:loans="clr-namespace:Gnomeshade.Avalonia.Core.Transactions.Loans;assembly=Gnomeshade.Avalonia.Core" | ||
xmlns:system="clr-namespace:System;assembly=System.Runtime" | ||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" | ||
d:DataContext="{x:Static dd:DesignTimeData.LoanMigrationViewModel}" | ||
x:Class="Gnomeshade.Desktop.Views.Transactions.Loans.Migration.LoanMigrationView" | ||
x:DataType="migration:LoanMigrationViewModel"> | ||
|
||
<Grid ColumnDefinitions="Auto,Auto,Auto" RowDefinitions="Auto,*"> | ||
|
||
<TextBlock Grid.Column="0" Grid.Row="0" Text="Legacy loans" HorizontalAlignment="Center"/> | ||
<DataGrid | ||
Grid.Column="0" Grid.Row="1" | ||
VerticalAlignment="Stretch" | ||
ItemsSource="{Binding Loans, Mode=TwoWay}" | ||
AutoGenerateColumns="False" IsReadOnly="False" | ||
CanUserReorderColumns="True" CanUserResizeColumns="True" CanUserSortColumns="True"> | ||
<DataGrid.Columns> | ||
<DataGridTextColumn | ||
x:DataType="loans:LoanOverview" | ||
Header="Issuer" IsReadOnly="True" | ||
Binding="{Binding IssuingCounterparty, Mode=OneWay}" /> | ||
<DataGridTextColumn | ||
x:DataType="loans:LoanOverview" | ||
Header="Receiver" IsReadOnly="True" | ||
Binding="{Binding ReceivingCounterparty, Mode=OneWay}" /> | ||
<DataGridTextColumn | ||
x:DataType="loans:LoanOverview" | ||
Header="Amount" IsReadOnly="True" | ||
Binding="{Binding Amount, Mode=OneWay, StringFormat=\{0:N2\}}" /> | ||
<DataGridTextColumn | ||
x:DataType="loans:LoanOverview" | ||
Header="Currency" IsReadOnly="True" | ||
Binding="{Binding Currency, Mode=OneWay}" /> | ||
</DataGrid.Columns> | ||
</DataGrid> | ||
|
||
<TextBlock Grid.Column="1" Grid.Row="0" Text="Loans with payments" HorizontalAlignment="Center"/> | ||
<DataGrid | ||
Grid.Column="1" Grid.Row="1" | ||
VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Visible" | ||
ItemsSource="{Binding MigratedLoans, Mode=TwoWay}" | ||
AutoGenerateColumns="False" IsReadOnly="False" | ||
CanUserReorderColumns="True" CanUserResizeColumns="True" CanUserSortColumns="True"> | ||
<DataGrid.Columns> | ||
<DataGridTemplateColumn Header="Name" IsReadOnly="True"> | ||
<DataTemplate x:DataType="migration:LoanMigrationRow"> | ||
<TextBlock | ||
HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5 0" | ||
Text="{Binding Name, Mode=OneWay}" /> | ||
Check warning on line 56 in source/Gnomeshade.Desktop/Views/Transactions/Loans/Migration/LoanMigrationView.axaml Codecov / codecov/patchsource/Gnomeshade.Desktop/Views/Transactions/Loans/Migration/LoanMigrationView.axaml#L55-L56
|
||
</DataTemplate> | ||
</DataGridTemplateColumn> | ||
<DataGridTemplateColumn Header="Issuer" IsReadOnly="True"> | ||
<DataTemplate x:DataType="migration:LoanMigrationRow"> | ||
<TextBlock | ||
HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5 0" | ||
Text="{Binding Issuer, Mode=OneWay}" /> | ||
Check warning on line 63 in source/Gnomeshade.Desktop/Views/Transactions/Loans/Migration/LoanMigrationView.axaml Codecov / codecov/patchsource/Gnomeshade.Desktop/Views/Transactions/Loans/Migration/LoanMigrationView.axaml#L62-L63
|
||
</DataTemplate> | ||
</DataGridTemplateColumn> | ||
<DataGridTemplateColumn Header="Receiver" IsReadOnly="True"> | ||
<DataTemplate x:DataType="migration:LoanMigrationRow"> | ||
<TextBlock | ||
HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5 0" | ||
Text="{Binding Receiver, Mode=OneWay}" /> | ||
Check warning on line 70 in source/Gnomeshade.Desktop/Views/Transactions/Loans/Migration/LoanMigrationView.axaml Codecov / codecov/patchsource/Gnomeshade.Desktop/Views/Transactions/Loans/Migration/LoanMigrationView.axaml#L69-L70
|
||
</DataTemplate> | ||
</DataGridTemplateColumn> | ||
<DataGridTemplateColumn Header="Currency" IsReadOnly="True"> | ||
<DataTemplate x:DataType="migration:LoanMigrationRow"> | ||
<TextBlock | ||
HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5 0" | ||
Text="{Binding Currency, Mode=OneWay}" /> | ||
Check warning on line 77 in source/Gnomeshade.Desktop/Views/Transactions/Loans/Migration/LoanMigrationView.axaml Codecov / codecov/patchsource/Gnomeshade.Desktop/Views/Transactions/Loans/Migration/LoanMigrationView.axaml#L76-L77
|
||
</DataTemplate> | ||
</DataGridTemplateColumn> | ||
<DataGridTemplateColumn Header="Payments" IsReadOnly="True"> | ||
<DataTemplate x:DataType="migration:LoanMigrationRow"> | ||
<ItemsControl ItemsSource="{Binding Amounts}"> | ||
<ItemsControl.ItemTemplate> | ||
<DataTemplate x:DataType="system:Decimal"> | ||
<TextBlock | ||
HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5 0" | ||
Text="{Binding ., Mode=OneWay, StringFormat=\{0:N2\}}" /> | ||
Check warning on line 87 in source/Gnomeshade.Desktop/Views/Transactions/Loans/Migration/LoanMigrationView.axaml Codecov / codecov/patchsource/Gnomeshade.Desktop/Views/Transactions/Loans/Migration/LoanMigrationView.axaml#L84-L87
|
||
</DataTemplate> | ||
</ItemsControl.ItemTemplate> | ||
</ItemsControl> | ||
|
||
</DataTemplate> | ||
</DataGridTemplateColumn> | ||
</DataGrid.Columns> | ||
</DataGrid> | ||
|
||
<!-- <UserControl Grid.Column="2" Content="{Binding Details}" /> --> | ||
</Grid> | ||
|
||
</UserControl> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright 2021 Valters Melnalksnis | ||
// Licensed under the GNU Affero General Public License v3.0 or later. | ||
// See LICENSE.txt file in the project root for full license information. | ||
|
||
using Avalonia.Controls; | ||
|
||
using Gnomeshade.Avalonia.Core; | ||
using Gnomeshade.Avalonia.Core.Transactions.Loans.Migration; | ||
|
||
namespace Gnomeshade.Desktop.Views.Transactions.Loans.Migration; | ||
|
||
/// <inheritdoc cref="LoanMigrationViewModel" /> | ||
public sealed partial class LoanMigrationView : UserControl, IView<LoanMigrationView, LoanMigrationViewModel> | ||
{ | ||
/// <summary>Initializes a new instance of the <see cref="LoanMigrationView"/> class.</summary> | ||
public LoanMigrationView() => InitializeComponent(); | ||
} |