PR U-10: split Home into StatementImport and PendingBookings
Home shrinks from ~200-line codebehind + 100-line markup to a 10-line orchestrator holding a @ref to <PendingBookings/> and wiring <StatementImport OnImported="RefreshPendingAsync"/> to it. Also renames IsCanceled -> cancelRequested and clears the Files dictionary after each import batch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
0c48539ce6
commit
97bf85d6a8
6 changed files with 315 additions and 272 deletions
|
|
@ -1,102 +1,9 @@
|
|||
@page "/"
|
||||
@page "/"
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<PageTitle>Home</PageTitle>
|
||||
|
||||
<FluentInputFile @ref="@myFileByBuffer"
|
||||
AnchorId="MyUploadBuffer"
|
||||
DragDropZoneVisible="false"
|
||||
Mode="InputFileMode.Buffer"
|
||||
Multiple="true"
|
||||
MaximumFileSize="@(10 * 1024 * 1024)"
|
||||
Accept=".zip, .xml"
|
||||
OnProgressChange="@OnProgressChangeAsync"
|
||||
OnCompleted="@OnCompletedAsync"/>
|
||||
<StatementImport OnImported="RefreshPendingAsync"/>
|
||||
|
||||
<FluentProgress Visible="@(progressPercent > 0)" Min="0" Max="100" Value="@progressPercent"/>
|
||||
<FluentLabel Alignment="HorizontalAlignment.Center">
|
||||
@progressTitle
|
||||
</FluentLabel>
|
||||
|
||||
<FluentButton Appearance="Appearance.Lightweight" IconStart="@(new Icons.Regular.Size16.ArrowUpload())"
|
||||
Id="MyUploadBuffer">
|
||||
Upload files
|
||||
</FluentButton>
|
||||
|
||||
<FluentButton Appearance="Appearance.Lightweight" IconStart="@(new Icons.Regular.Size16.Dismiss())"
|
||||
@onclick="@((e) => IsCanceled = true)">
|
||||
Cancel
|
||||
</FluentButton>
|
||||
|
||||
@if (matchingBankTransactions == null || matchingBankTransactions.Count == 0)
|
||||
{
|
||||
<p>Keine Transaktionen vorhanden!</p>
|
||||
<FluentEmoji Value="@(new Emojis.SmileysEmotion.Color.Default.MoneyMouthFace())" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Buchungsdatum</th>
|
||||
<th>Valutadatum</th>
|
||||
<th>Zusatzinfo</th>
|
||||
<th>Text</th>
|
||||
<th>Konto</th>
|
||||
<th>Betrag</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 class="zusatzinfo-column" title="@transaction.BankTransactionText">
|
||||
<div>@transaction.BankTransactionText</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-column">
|
||||
<input list="texts" @bind="@transaction.Text" placeholder="Type or select..." readonly="@transaction.ReadOnly"/>
|
||||
<datalist id="texts">
|
||||
@foreach (var text in bookingTexts.OrderBy(t => t))
|
||||
{
|
||||
<option value="@text"></option>
|
||||
}
|
||||
</datalist>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@if (transaction.Value > 0)
|
||||
{
|
||||
<AccountSelector Accounts="accounts" @bind-AccountId="@transaction.OriginAccountId" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<AccountSelector Accounts="accounts" @bind-AccountId="@transaction.TargetAccountId" />
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<div>@Math.Abs(transaction.Value).ToFormattedString()</div>
|
||||
</td>
|
||||
<td style="align-content: center">
|
||||
<FluentButton IconEnd="@(new Icons.Regular.Size16.SplitVertical())" Appearance="Appearance.Neutral"
|
||||
OnClick="@(() => SplitBankTransactionAsync(transaction))" Disabled="@(transaction.Value > 0 || transaction.ReadOnly)" />
|
||||
</td>
|
||||
<td style="align-content: center">
|
||||
<FluentButton IconEnd="@(new Icons.Regular.Size16.Save())" Appearance="Appearance.Neutral"
|
||||
OnClick="@(() => BookBankTransactionAsync(transaction))"
|
||||
Disabled="@transaction.ReadOnly" />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
<PendingBookings @ref="pendingBookings"/>
|
||||
|
|
|
|||
|
|
@ -1,188 +1,16 @@
|
|||
using System.IO.Compression;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.FluentUI.AspNetCore.Components;
|
||||
using Schaad.Accounting.Datasets;
|
||||
using Schaad.Accounting.Interfaces;
|
||||
using Schaad.Accounting.Models;
|
||||
using Schaad.Accounting.UI.Components.Pages.Dialogs;
|
||||
|
||||
namespace Schaad.Accounting.UI.Components.Pages;
|
||||
|
||||
public partial class Home : ComponentBase
|
||||
{
|
||||
[Inject]
|
||||
private IViewService viewService { get; set; } = null!;
|
||||
private PendingBookings? pendingBookings;
|
||||
|
||||
[Inject]
|
||||
private IToastService toastService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IMessageService messageService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IDialogService dialogService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IFileService fileService { get; set; } = null!;
|
||||
|
||||
[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();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
private async Task RefreshPendingAsync()
|
||||
{
|
||||
await LoadDataAsync();
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
private async Task LoadDataAsync()
|
||||
if (pendingBookings is not null)
|
||||
{
|
||||
matchingBankTransactions = await viewService.MatchOpenBankTransactionsAsync();
|
||||
accounts = await viewService.GetAccountViewListAsync();
|
||||
bookingTexts = (await bookingTextRepository.GetBookingTextListAsync()).Select(b => b.Text).ToArray();
|
||||
}
|
||||
|
||||
async Task OnProgressChangeAsync(FluentInputFileEventArgs file)
|
||||
{
|
||||
progressPercent = file.ProgressPercent;
|
||||
progressTitle = file.ProgressTitle;
|
||||
|
||||
// To cancel?
|
||||
file.IsCancelled = IsCanceled;
|
||||
|
||||
// New file
|
||||
if (!Files.ContainsKey(file.Index))
|
||||
{
|
||||
var localFile = Path.GetTempFileName() + file.Name;
|
||||
Files.Add(file.Index, localFile);
|
||||
}
|
||||
|
||||
// Write to the FileStream
|
||||
await file.Buffer.AppendToFileAsync(Files[file.Index]);
|
||||
}
|
||||
|
||||
private async Task OnCompletedAsync(IEnumerable<FluentInputFileEventArgs> files)
|
||||
{
|
||||
progressPercent = myFileByBuffer!.ProgressPercent;
|
||||
progressTitle = myFileByBuffer!.ProgressTitle;
|
||||
|
||||
foreach (var file in Files)
|
||||
{
|
||||
var extension = Path.GetExtension(file.Value);
|
||||
if (extension is ".xml")
|
||||
{
|
||||
await ImportXmlAndShowResultAsync(file.Value);
|
||||
}
|
||||
else if (extension is ".zip")
|
||||
{
|
||||
var unzippedFiles = UnzipAndListFiles(file.Value);
|
||||
foreach (var unzippedFile in unzippedFiles)
|
||||
{
|
||||
await ImportXmlAndShowResultAsync(unzippedFile);
|
||||
File.Delete(unzippedFile);
|
||||
}
|
||||
}
|
||||
|
||||
File.Delete(file.Value);
|
||||
}
|
||||
|
||||
// Imports may update the bank-account balance and add new bank transactions
|
||||
// — reload everything so the pending list, account balances, and booking-text
|
||||
// suggestions stay consistent.
|
||||
await LoadDataAsync();
|
||||
}
|
||||
|
||||
private async Task ImportXmlAndShowResultAsync(string fileName)
|
||||
{
|
||||
var messages = await fileService.ImportAccountStatementFileAsync(fileName);
|
||||
foreach (var message in messages)
|
||||
{
|
||||
await ShowImportResultAsync(message);
|
||||
}
|
||||
}
|
||||
|
||||
private IReadOnlyList<string> UnzipAndListFiles(string zipFilePath)
|
||||
{
|
||||
var tempExtractPath = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));
|
||||
Directory.CreateDirectory(tempExtractPath);
|
||||
ZipFile.ExtractToDirectory(zipFilePath, tempExtractPath);
|
||||
return new List<string>(Directory.GetFiles(tempExtractPath, "*", SearchOption.AllDirectories));
|
||||
}
|
||||
|
||||
private async Task ShowImportResultAsync(MessageDataset message)
|
||||
{
|
||||
var joinedDetails = string.Join(", ", message.Lines);
|
||||
var text = $"{message.Title}: {joinedDetails}";
|
||||
|
||||
if (message.Status == MessageStatus.Success)
|
||||
{
|
||||
ShowToast(text, ToastIntent.Success);
|
||||
}
|
||||
else if (message.Status == MessageStatus.Info)
|
||||
{
|
||||
ShowToast(text, ToastIntent.Info);
|
||||
}
|
||||
else if (message.Status == MessageStatus.Warning)
|
||||
{
|
||||
await ShowMessageAsync(text, MessageIntent.Warning);
|
||||
}
|
||||
else if (message.Status == MessageStatus.Error)
|
||||
{
|
||||
await ShowMessageAsync(text, MessageIntent.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ShowMessageAsync(string message, MessageIntent intent)
|
||||
{
|
||||
await messageService.ShowMessageBarAsync(message, intent, "MESSAGES_TOP");
|
||||
}
|
||||
|
||||
private void ShowToast(string message, ToastIntent intent)
|
||||
{
|
||||
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;
|
||||
}
|
||||
await transactionRepository.SaveTransactionAsync(transaction);
|
||||
transaction.ReadOnly = true;
|
||||
}
|
||||
|
||||
private async Task SplitBankTransactionAsync(Transaction transaction)
|
||||
{
|
||||
var dialog = await dialogService.ShowDialogAsync<TransactionSplitDialog>(transaction, new DialogParameters()
|
||||
{
|
||||
Height = "500px",
|
||||
Width = "800px",
|
||||
Title = "Split Transaktion",
|
||||
PreventDismissOnOverlayClick = true,
|
||||
PreventScroll = true,
|
||||
});
|
||||
|
||||
var result = await dialog.Result;
|
||||
if (!result.Cancelled && result.Data != null)
|
||||
{
|
||||
// Re-run the auto-matcher in-place so the newly created split
|
||||
// transactions disappear from the pending list. No forceLoad — the
|
||||
// SignalR circuit stays up and the scoped RepositoryCache survives.
|
||||
await LoadDataAsync();
|
||||
await pendingBookings.RefreshAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
73
Schaad.Accounting.UI/Components/Pages/PendingBookings.razor
Normal file
73
Schaad.Accounting.UI/Components/Pages/PendingBookings.razor
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
@rendermode InteractiveServer
|
||||
|
||||
@if (matchingBankTransactions is null || matchingBankTransactions.Count == 0)
|
||||
{
|
||||
<p>Keine Transaktionen vorhanden!</p>
|
||||
<FluentEmoji Value="@(new Emojis.SmileysEmotion.Color.Default.MoneyMouthFace())" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Buchungsdatum</th>
|
||||
<th>Valutadatum</th>
|
||||
<th>Zusatzinfo</th>
|
||||
<th>Text</th>
|
||||
<th>Konto</th>
|
||||
<th>Betrag</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 class="zusatzinfo-column" title="@transaction.BankTransactionText">
|
||||
<div>@transaction.BankTransactionText</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-column">
|
||||
<input list="texts" @bind="@transaction.Text" placeholder="Type or select..." readonly="@transaction.ReadOnly"/>
|
||||
<datalist id="texts">
|
||||
@foreach (var text in bookingTexts.OrderBy(t => t))
|
||||
{
|
||||
<option value="@text"></option>
|
||||
}
|
||||
</datalist>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@if (transaction.Value > 0)
|
||||
{
|
||||
<AccountSelector Accounts="accounts" @bind-AccountId="@transaction.OriginAccountId" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<AccountSelector Accounts="accounts" @bind-AccountId="@transaction.TargetAccountId" />
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<div>@Math.Abs(transaction.Value).ToFormattedString()</div>
|
||||
</td>
|
||||
<td style="align-content: center">
|
||||
<FluentButton IconEnd="@(new Icons.Regular.Size16.SplitVertical())" Appearance="Appearance.Neutral"
|
||||
OnClick="@(() => SplitBankTransactionAsync(transaction))" Disabled="@(transaction.Value > 0 || transaction.ReadOnly)" />
|
||||
</td>
|
||||
<td style="align-content: center">
|
||||
<FluentButton IconEnd="@(new Icons.Regular.Size16.Save())" Appearance="Appearance.Neutral"
|
||||
OnClick="@(() => BookBankTransactionAsync(transaction))"
|
||||
Disabled="@transaction.ReadOnly" />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.FluentUI.AspNetCore.Components;
|
||||
using Schaad.Accounting.Interfaces;
|
||||
using Schaad.Accounting.Models;
|
||||
using Schaad.Accounting.UI.Components.Pages.Dialogs;
|
||||
|
||||
namespace Schaad.Accounting.UI.Components.Pages;
|
||||
|
||||
public partial class PendingBookings : ComponentBase
|
||||
{
|
||||
[Inject]
|
||||
private IViewService viewService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ITransactionRepository transactionRepository { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IBookingTextRepository bookingTextRepository { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IDialogService dialogService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IToastService toastService { get; set; } = null!;
|
||||
|
||||
private List<Transaction>? matchingBankTransactions;
|
||||
private IReadOnlyList<Account> accounts = [];
|
||||
private IReadOnlyList<string> bookingTexts = [];
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadDataAsync();
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-run the auto-matcher and reload accounts / booking texts. Called from
|
||||
/// the parent (Home) after a statement import completes.
|
||||
/// </summary>
|
||||
public async Task RefreshAsync()
|
||||
{
|
||||
await LoadDataAsync();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
matchingBankTransactions = await viewService.MatchOpenBankTransactionsAsync();
|
||||
accounts = await viewService.GetAccountViewListAsync();
|
||||
bookingTexts = (await bookingTextRepository.GetBookingTextListAsync()).Select(b => b.Text).ToArray();
|
||||
}
|
||||
|
||||
private async Task BookBankTransactionAsync(Transaction transaction)
|
||||
{
|
||||
if (string.IsNullOrEmpty(transaction.Text) || string.IsNullOrEmpty(transaction.TargetAccountId))
|
||||
{
|
||||
toastService.ShowToast(ToastIntent.Error, "Transaktion kann wegen fehlenden Daten nicht gespeichert werden!", 3000);
|
||||
return;
|
||||
}
|
||||
await transactionRepository.SaveTransactionAsync(transaction);
|
||||
transaction.ReadOnly = true;
|
||||
}
|
||||
|
||||
private async Task SplitBankTransactionAsync(Transaction transaction)
|
||||
{
|
||||
var dialog = await dialogService.ShowDialogAsync<TransactionSplitDialog>(transaction, new DialogParameters()
|
||||
{
|
||||
Height = "500px",
|
||||
Width = "800px",
|
||||
Title = "Split Transaktion",
|
||||
PreventDismissOnOverlayClick = true,
|
||||
PreventScroll = true,
|
||||
});
|
||||
|
||||
var result = await dialog.Result;
|
||||
if (!result.Cancelled && result.Data != null)
|
||||
{
|
||||
await LoadDataAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Schaad.Accounting.UI/Components/Pages/StatementImport.razor
Normal file
26
Schaad.Accounting.UI/Components/Pages/StatementImport.razor
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
@rendermode InteractiveServer
|
||||
|
||||
<FluentInputFile @ref="@myFileByBuffer"
|
||||
AnchorId="MyUploadBuffer"
|
||||
DragDropZoneVisible="false"
|
||||
Mode="InputFileMode.Buffer"
|
||||
Multiple="true"
|
||||
MaximumFileSize="@(10 * 1024 * 1024)"
|
||||
Accept=".zip, .xml"
|
||||
OnProgressChange="@OnProgressChangeAsync"
|
||||
OnCompleted="@OnCompletedAsync"/>
|
||||
|
||||
<FluentProgress Visible="@(progressPercent > 0)" Min="0" Max="100" Value="@progressPercent"/>
|
||||
<FluentLabel Alignment="HorizontalAlignment.Center">
|
||||
@progressTitle
|
||||
</FluentLabel>
|
||||
|
||||
<FluentButton Appearance="Appearance.Lightweight" IconStart="@(new Icons.Regular.Size16.ArrowUpload())"
|
||||
Id="MyUploadBuffer">
|
||||
Upload files
|
||||
</FluentButton>
|
||||
|
||||
<FluentButton Appearance="Appearance.Lightweight" IconStart="@(new Icons.Regular.Size16.Dismiss())"
|
||||
@onclick="@((e) => cancelRequested = true)">
|
||||
Cancel
|
||||
</FluentButton>
|
||||
128
Schaad.Accounting.UI/Components/Pages/StatementImport.razor.cs
Normal file
128
Schaad.Accounting.UI/Components/Pages/StatementImport.razor.cs
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
using System.IO.Compression;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.FluentUI.AspNetCore.Components;
|
||||
using Schaad.Accounting.Datasets;
|
||||
using Schaad.Accounting.Interfaces;
|
||||
|
||||
namespace Schaad.Accounting.UI.Components.Pages;
|
||||
|
||||
public partial class StatementImport : ComponentBase
|
||||
{
|
||||
[Inject]
|
||||
private IFileService fileService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IToastService toastService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IMessageService messageService { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Fires after all uploaded files have been processed, so the parent can
|
||||
/// refresh dependent views (e.g. re-run the auto-matcher on Home).
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback OnImported { get; set; }
|
||||
|
||||
private FluentInputFile? myFileByBuffer;
|
||||
private int? progressPercent;
|
||||
private string? progressTitle;
|
||||
private bool cancelRequested;
|
||||
private Dictionary<int, string> Files = new();
|
||||
|
||||
private async Task OnProgressChangeAsync(FluentInputFileEventArgs file)
|
||||
{
|
||||
progressPercent = file.ProgressPercent;
|
||||
progressTitle = file.ProgressTitle;
|
||||
|
||||
file.IsCancelled = cancelRequested;
|
||||
|
||||
if (!Files.ContainsKey(file.Index))
|
||||
{
|
||||
var localFile = Path.GetTempFileName() + file.Name;
|
||||
Files.Add(file.Index, localFile);
|
||||
}
|
||||
|
||||
await file.Buffer.AppendToFileAsync(Files[file.Index]);
|
||||
}
|
||||
|
||||
private async Task OnCompletedAsync(IEnumerable<FluentInputFileEventArgs> files)
|
||||
{
|
||||
progressPercent = myFileByBuffer!.ProgressPercent;
|
||||
progressTitle = myFileByBuffer!.ProgressTitle;
|
||||
|
||||
foreach (var file in Files)
|
||||
{
|
||||
var extension = Path.GetExtension(file.Value);
|
||||
if (extension is ".xml")
|
||||
{
|
||||
await ImportXmlAndShowResultAsync(file.Value);
|
||||
}
|
||||
else if (extension is ".zip")
|
||||
{
|
||||
var unzippedFiles = UnzipAndListFiles(file.Value);
|
||||
foreach (var unzippedFile in unzippedFiles)
|
||||
{
|
||||
await ImportXmlAndShowResultAsync(unzippedFile);
|
||||
File.Delete(unzippedFile);
|
||||
}
|
||||
}
|
||||
|
||||
File.Delete(file.Value);
|
||||
}
|
||||
|
||||
Files.Clear();
|
||||
|
||||
await OnImported.InvokeAsync();
|
||||
}
|
||||
|
||||
private async Task ImportXmlAndShowResultAsync(string fileName)
|
||||
{
|
||||
var messages = await fileService.ImportAccountStatementFileAsync(fileName);
|
||||
foreach (var message in messages)
|
||||
{
|
||||
await ShowImportResultAsync(message);
|
||||
}
|
||||
}
|
||||
|
||||
private IReadOnlyList<string> UnzipAndListFiles(string zipFilePath)
|
||||
{
|
||||
var tempExtractPath = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));
|
||||
Directory.CreateDirectory(tempExtractPath);
|
||||
ZipFile.ExtractToDirectory(zipFilePath, tempExtractPath);
|
||||
return new List<string>(Directory.GetFiles(tempExtractPath, "*", SearchOption.AllDirectories));
|
||||
}
|
||||
|
||||
private async Task ShowImportResultAsync(MessageDataset message)
|
||||
{
|
||||
var joinedDetails = string.Join(", ", message.Lines);
|
||||
var text = $"{message.Title}: {joinedDetails}";
|
||||
|
||||
if (message.Status == MessageStatus.Success)
|
||||
{
|
||||
ShowToast(text, ToastIntent.Success);
|
||||
}
|
||||
else if (message.Status == MessageStatus.Info)
|
||||
{
|
||||
ShowToast(text, ToastIntent.Info);
|
||||
}
|
||||
else if (message.Status == MessageStatus.Warning)
|
||||
{
|
||||
await ShowMessageAsync(text, MessageIntent.Warning);
|
||||
}
|
||||
else if (message.Status == MessageStatus.Error)
|
||||
{
|
||||
await ShowMessageAsync(text, MessageIntent.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ShowMessageAsync(string message, MessageIntent intent)
|
||||
{
|
||||
await messageService.ShowMessageBarAsync(message, intent, "MESSAGES_TOP");
|
||||
}
|
||||
|
||||
private void ShowToast(string message, ToastIntent intent)
|
||||
{
|
||||
toastService.ShowToast(intent, message, 3000);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue