Show bankpayment matching
This commit is contained in:
parent
0828dd7691
commit
0c537f8816
7 changed files with 148 additions and 40 deletions
|
|
@ -1,15 +0,0 @@
|
||||||
using System.Globalization;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Schaad.Accounting.Common.Extensions
|
|
||||||
{
|
|
||||||
public static class DecimalExtension
|
|
||||||
{
|
|
||||||
public static string ToFormattedString(this decimal value)
|
|
||||||
{
|
|
||||||
var culture = new CultureInfo("de-CH");
|
|
||||||
culture.NumberFormat.NumberGroupSeparator = "'";
|
|
||||||
return value.ToString("#,0.00", culture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -14,4 +14,7 @@
|
||||||
<HintPath>..\dlls\Schaad.Finance.Api.dll</HintPath>
|
<HintPath>..\dlls\Schaad.Finance.Api.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Interfaces\Extensions\" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Schaad.Accounting.Common.Extensions;
|
|
||||||
using Schaad.Accounting.Datasets;
|
using Schaad.Accounting.Datasets;
|
||||||
using Schaad.Accounting.Interfaces;
|
using Schaad.Accounting.Interfaces;
|
||||||
using Schaad.Finance.Api;
|
using Schaad.Finance.Api;
|
||||||
|
|
@ -133,18 +133,25 @@ namespace Schaad.Accounting.Services
|
||||||
|
|
||||||
if (trx.OriginAccountId == accountId)
|
if (trx.OriginAccountId == accountId)
|
||||||
{
|
{
|
||||||
debit = trx.Value.ToFormattedString();
|
debit = ToFormattedString(trx.Value);
|
||||||
trx.Value *= -1;
|
trx.Value *= -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
credit = trx.Value.ToFormattedString();
|
credit = ToFormattedString(trx.Value);
|
||||||
}
|
}
|
||||||
balance += trx.Value;
|
balance += trx.Value;
|
||||||
sb.AppendLine($"{trx.BookingDate:dd.MM.yyyy};{trx.ValueDate:dd.MM.yyyy};{trx.Text};{debit};{credit};{balance.ToFormattedString()}");
|
sb.AppendLine($"{trx.BookingDate:dd.MM.yyyy};{trx.ValueDate:dd.MM.yyyy};{trx.Text};{debit};{credit};{ToFormattedString(balance)}");
|
||||||
}
|
}
|
||||||
var fileBytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(sb.ToString());
|
var fileBytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(sb.ToString());
|
||||||
return fileBytes;
|
return fileBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string ToFormattedString(decimal value)
|
||||||
|
{
|
||||||
|
var culture = new CultureInfo("de-CH");
|
||||||
|
culture.NumberFormat.NumberGroupSeparator = "'";
|
||||||
|
return value.ToString("#,0.00", culture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
@page "/"
|
@page "/"
|
||||||
|
|
||||||
@rendermode InteractiveServer
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
<PageTitle>Home</PageTitle>
|
<PageTitle>Home</PageTitle>
|
||||||
|
|
@ -26,19 +27,53 @@
|
||||||
Cancel
|
Cancel
|
||||||
</FluentButton>
|
</FluentButton>
|
||||||
|
|
||||||
<div id="datagrid-container">
|
<table>
|
||||||
<FluentDataGrid Items="@bankTransactionsQueryable" RowSize="DataGridRowSize.Small" AutoItemsPerPage="true">
|
<thead>
|
||||||
<ChildContent>
|
<tr>
|
||||||
<PropertyColumn Title="Buchungsdatum" Width="170px" Property="@(p => p.BookingDate)" Format="dd.MM.yyyy" Sortable="true" IsDefaultSortColumn="true" InitialSortDirection="SortDirection.Descending"/>
|
<th>Buchungsdatum</th>
|
||||||
<PropertyColumn Title="Valutadatum" Width="170px" Property="@(p => p.ValueDate)" Format="dd.MM.yyyy" Sortable="true"/>
|
<th>Valutadatum</th>
|
||||||
<PropertyColumn Title="Text" Property="@(p => p.Text)"/>
|
<th>Text</th>
|
||||||
<PropertyColumn Title="Betrag" Width="150px" Property="@(p => p.Value)" Format="C2" Align="Align.End"/>
|
<th>Betrag</th>
|
||||||
</ChildContent>
|
<th>Konto</th>
|
||||||
<EmptyContent>
|
<th>Zusatzinfo</th>
|
||||||
<FluentStack Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center" HorizontalAlignment="HorizontalAlignment.Center" HorizontalGap="10">
|
<th></th>
|
||||||
<FluentIcon Value="@(new Icons.Filled.Size24.Crown())" Color="@Color.Accent"/>
|
<th></th>
|
||||||
<div>Keine Daten vorhanden!</div>
|
</tr>
|
||||||
</FluentStack>
|
</thead>
|
||||||
</EmptyContent>
|
<tbody>
|
||||||
</FluentDataGrid>
|
@foreach (var transaction in matchingBankTransactions)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td><div>@transaction.BookingDate.ToString("dd.MM.yyyy")</div></td>
|
||||||
|
<td><div>@transaction.ValueDate.ToString("dd.MM.yyyy")</div></td>
|
||||||
|
<td>
|
||||||
|
<div class="text-column">
|
||||||
|
<input list="texts" @bind="@transaction.Text" placeholder="Type or select..." />
|
||||||
|
<datalist id="texts">
|
||||||
|
@foreach (var text in bookingTexts.OrderBy(t => t))
|
||||||
|
{
|
||||||
|
<option value="@text"></option>
|
||||||
|
}
|
||||||
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="betrag-column">
|
||||||
|
<div>@transaction.Value.ToFormattedString()</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select @bind="@transaction.TargetAccountId">
|
||||||
|
@foreach (var account in accounts.OrderBy(a=> a.Number).ThenBy(a => a.Name))
|
||||||
|
{
|
||||||
|
<option value="@account.Id">@($"{account.Number} / {@account.Name}")</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="zusatzinfo-column" title="@transaction.BankTransactionText">
|
||||||
|
<div>@transaction.BankTransactionText</div>
|
||||||
|
</td>
|
||||||
|
<td style="align-content: center"><FluentButton IconEnd="@(new Icons.Regular.Size16.SplitVertical())" Appearance="Appearance.Neutral" OnClick="@(() => SplitBankTransactionAsync(transaction))"/></td>
|
||||||
|
<td style="align-content: center"><FluentButton IconEnd="@(new Icons.Regular.Size16.Save())" Appearance="Appearance.Neutral" OnClick="@(() => BookBankTransactionAsync(transaction))"/></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.IO.Compression;
|
using System.Diagnostics;
|
||||||
|
using System.IO.Compression;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.FluentUI.AspNetCore.Components;
|
using Microsoft.FluentUI.AspNetCore.Components;
|
||||||
using Schaad.Accounting.Datasets;
|
using Schaad.Accounting.Datasets;
|
||||||
|
|
@ -24,7 +25,15 @@ public partial class Home : ComponentBase
|
||||||
[Inject]
|
[Inject]
|
||||||
private IFileService fileService { get; set; } = null!;
|
private IFileService fileService { get; set; } = null!;
|
||||||
|
|
||||||
private IQueryable<BankTransaction>? bankTransactionsQueryable;
|
[Inject]
|
||||||
|
private IBookingTextRepository bookingTextRepository { get; set; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ITransactionRepository transactionRepository { get; set; } = null!;
|
||||||
|
|
||||||
|
private List<Transaction>? matchingBankTransactions;
|
||||||
|
private IReadOnlyList<Account> accounts = [];
|
||||||
|
private IReadOnlyList<string> bookingTexts = [];
|
||||||
|
|
||||||
FluentInputFile? myFileByBuffer;
|
FluentInputFile? myFileByBuffer;
|
||||||
int? progressPercent;
|
int? progressPercent;
|
||||||
|
|
@ -32,9 +41,17 @@ public partial class Home : ComponentBase
|
||||||
bool IsCanceled;
|
bool IsCanceled;
|
||||||
Dictionary<int, string> Files = new();
|
Dictionary<int, string> Files = new();
|
||||||
|
|
||||||
|
private bool loaded;
|
||||||
|
|
||||||
protected override Task OnInitializedAsync()
|
protected override Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
bankTransactionsQueryable = viewService.GetOpenBankTransactionList().AsQueryable();
|
if (loaded == false) {
|
||||||
|
matchingBankTransactions = viewService.MatchOpenBankTransactions();
|
||||||
|
accounts = viewService.GetAccountViewList();
|
||||||
|
bookingTexts = bookingTextRepository.GetBookingTextList().Select(b => b.Text).ToArray();
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
return base.OnInitializedAsync();
|
return base.OnInitializedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,7 +99,7 @@ public partial class Home : ComponentBase
|
||||||
File.Delete(file.Value);
|
File.Delete(file.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bankTransactionsQueryable = viewService.GetOpenBankTransactionList().AsQueryable();
|
matchingBankTransactions = viewService.MatchOpenBankTransactions();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ImportXmlAndShowResultAsync(string fileName)
|
private async Task ImportXmlAndShowResultAsync(string fileName)
|
||||||
|
|
@ -134,4 +151,22 @@ public partial class Home : ComponentBase
|
||||||
{
|
{
|
||||||
toastService.ShowToast(intent, message, 3000);
|
toastService.ShowToast(intent, message, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task BookBankTransactionAsync(Transaction transaction)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(transaction.Text) || string.IsNullOrEmpty(transaction.TargetAccountId))
|
||||||
|
{
|
||||||
|
ShowToast("Transaktion kann wegen fehlenden Daten nicht gespeichert werden!", ToastIntent.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
transactionRepository.SaveTransaction(transaction);
|
||||||
|
matchingBankTransactions!.RemoveAll(q => q == transaction);
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SplitBankTransactionAsync(Transaction transaction)
|
||||||
|
{
|
||||||
|
ShowToast("Split noch nicht implementiert!", ToastIntent.Warning);
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
42
Schaad.Accounting.UI/Components/Pages/Home.razor.css
Normal file
42
Schaad.Accounting.UI/Components/Pages/Home.razor.css
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 4px 4px;
|
||||||
|
vertical-align: middle;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
th:nth-child(1), td:nth-child(1) { width: 120px; } /* Buchungsdatum */
|
||||||
|
th:nth-child(2), td:nth-child(2) { width: 120px; } /* Valutadatum */
|
||||||
|
th:nth-child(3), td:nth-child(3) { width: 240px; } /* Text */
|
||||||
|
th:nth-child(4), td:nth-child(4) { width: 100px; text-align: right; } /* Betrag */
|
||||||
|
th:nth-child(5), td:nth-child(5) { width: 300px; } /* Konto */
|
||||||
|
th:nth-child(7), td:nth-child(7),
|
||||||
|
th:nth-child(8), td:nth-child(8) { width: 40px; text-align: center; } /* Buttons */
|
||||||
|
|
||||||
|
.text-column input {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box; /* Ensures padding/border doesn't overflow */
|
||||||
|
}
|
||||||
|
|
||||||
|
.zusatzinfo-column div {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.betrag-column {
|
||||||
|
text-align: right;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
@ -13,5 +13,6 @@
|
||||||
@using Plotly.Blazor
|
@using Plotly.Blazor
|
||||||
@using Schaad.Accounting.UI
|
@using Schaad.Accounting.UI
|
||||||
@using Schaad.Accounting.UI.Components
|
@using Schaad.Accounting.UI.Components
|
||||||
|
@using Schaad.Accounting.Models
|
||||||
@using Schaad.Accounting.Interfaces
|
@using Schaad.Accounting.Interfaces
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue