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>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Interfaces\Extensions\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Schaad.Accounting.Common.Extensions;
|
||||
using Schaad.Accounting.Datasets;
|
||||
using Schaad.Accounting.Interfaces;
|
||||
using Schaad.Finance.Api;
|
||||
|
|
@ -133,18 +133,25 @@ namespace Schaad.Accounting.Services
|
|||
|
||||
if (trx.OriginAccountId == accountId)
|
||||
{
|
||||
debit = trx.Value.ToFormattedString();
|
||||
debit = ToFormattedString(trx.Value);
|
||||
trx.Value *= -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
credit = trx.Value.ToFormattedString();
|
||||
credit = ToFormattedString(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());
|
||||
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 "/"
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<PageTitle>Home</PageTitle>
|
||||
|
|
@ -26,19 +27,53 @@
|
|||
Cancel
|
||||
</FluentButton>
|
||||
|
||||
<div id="datagrid-container">
|
||||
<FluentDataGrid Items="@bankTransactionsQueryable" RowSize="DataGridRowSize.Small" AutoItemsPerPage="true">
|
||||
<ChildContent>
|
||||
<PropertyColumn Title="Buchungsdatum" Width="170px" Property="@(p => p.BookingDate)" Format="dd.MM.yyyy" Sortable="true" IsDefaultSortColumn="true" InitialSortDirection="SortDirection.Descending"/>
|
||||
<PropertyColumn Title="Valutadatum" Width="170px" Property="@(p => p.ValueDate)" Format="dd.MM.yyyy" Sortable="true"/>
|
||||
<PropertyColumn Title="Text" Property="@(p => p.Text)"/>
|
||||
<PropertyColumn Title="Betrag" Width="150px" Property="@(p => p.Value)" Format="C2" Align="Align.End"/>
|
||||
</ChildContent>
|
||||
<EmptyContent>
|
||||
<FluentStack Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center" HorizontalAlignment="HorizontalAlignment.Center" HorizontalGap="10">
|
||||
<FluentIcon Value="@(new Icons.Filled.Size24.Crown())" Color="@Color.Accent"/>
|
||||
<div>Keine Daten vorhanden!</div>
|
||||
</FluentStack>
|
||||
</EmptyContent>
|
||||
</FluentDataGrid>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Buchungsdatum</th>
|
||||
<th>Valutadatum</th>
|
||||
<th>Text</th>
|
||||
<th>Betrag</th>
|
||||
<th>Konto</th>
|
||||
<th>Zusatzinfo</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@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>
|
||||
</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.FluentUI.AspNetCore.Components;
|
||||
using Schaad.Accounting.Datasets;
|
||||
|
|
@ -24,17 +25,33 @@ public partial class Home : ComponentBase
|
|||
[Inject]
|
||||
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;
|
||||
int? progressPercent;
|
||||
string? progressTitle;
|
||||
bool IsCanceled;
|
||||
Dictionary<int, string> Files = new();
|
||||
|
||||
|
||||
private bool loaded;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
@ -82,7 +99,7 @@ public partial class Home : ComponentBase
|
|||
File.Delete(file.Value);
|
||||
}
|
||||
|
||||
bankTransactionsQueryable = viewService.GetOpenBankTransactionList().AsQueryable();
|
||||
matchingBankTransactions = viewService.MatchOpenBankTransactions();
|
||||
}
|
||||
|
||||
private async Task ImportXmlAndShowResultAsync(string fileName)
|
||||
|
|
@ -134,4 +151,22 @@ public partial class Home : ComponentBase
|
|||
{
|
||||
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 Schaad.Accounting.UI
|
||||
@using Schaad.Accounting.UI.Components
|
||||
@using Schaad.Accounting.Models
|
||||
@using Schaad.Accounting.Interfaces
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue