Implement backup and restore functionality; add IBackupService and BackupService; refactor services to use DbContextFactory
This commit is contained in:
@@ -12,12 +12,15 @@ public static class DependencyInjection
|
||||
{
|
||||
services.AddDbContext<FinanceDbContext>(options =>
|
||||
options.UseSqlite(connectionString));
|
||||
services.AddDbContextFactory<FinanceDbContext>(options =>
|
||||
options.UseSqlite(connectionString));
|
||||
|
||||
services.AddScoped<IAccountService, AccountService>();
|
||||
services.AddScoped<IEntryService, EntryService>();
|
||||
services.AddScoped<IBalanceQueryService, BalanceQueryService>();
|
||||
services.AddScoped<IPdfStatementService, PdfStatementService>();
|
||||
services.AddScoped<IFileSaveService, FileSaveService>();
|
||||
services.AddSingleton<IBackupService, BackupService>();
|
||||
services.AddSingleton<ISettingsService, SettingsService>();
|
||||
|
||||
return services;
|
||||
|
||||
@@ -9,13 +9,15 @@ namespace Duempelkas.Infrastructure.Services;
|
||||
|
||||
public class AccountService : IAccountService
|
||||
{
|
||||
private readonly FinanceDbContext _db;
|
||||
private readonly IDbContextFactory<FinanceDbContext> _dbFactory;
|
||||
|
||||
public AccountService(FinanceDbContext db) => _db = db;
|
||||
public AccountService(IDbContextFactory<FinanceDbContext> dbFactory) => _dbFactory = dbFactory;
|
||||
|
||||
public async Task<List<AccountSummaryDto>> GetAllAccountsAsync()
|
||||
{
|
||||
var accounts = await _db.Accounts
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
var accounts = await db.Accounts
|
||||
.Include(a => a.Entries)
|
||||
.OrderBy(a => a.Name)
|
||||
.ToListAsync();
|
||||
@@ -25,7 +27,9 @@ public class AccountService : IAccountService
|
||||
|
||||
public async Task<AccountSummaryDto> GetAccountAsync(int accountId)
|
||||
{
|
||||
var account = await _db.Accounts
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
var account = await db.Accounts
|
||||
.Include(a => a.Entries)
|
||||
.FirstOrDefaultAsync(a => a.Id == accountId)
|
||||
?? throw new InvalidOperationException($"Account {accountId} not found.");
|
||||
@@ -35,51 +39,59 @@ public class AccountService : IAccountService
|
||||
|
||||
public async Task<AccountSummaryDto> CreateAccountAsync(string name)
|
||||
{
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
var account = new Account { Name = name };
|
||||
_db.Accounts.Add(account);
|
||||
await _db.SaveChangesAsync();
|
||||
db.Accounts.Add(account);
|
||||
await db.SaveChangesAsync();
|
||||
return new AccountSummaryDto(account.Id, account.Name, 0m, 0m, account.CreatedUtc);
|
||||
}
|
||||
|
||||
public async Task RenameAccountAsync(int accountId, string newName)
|
||||
{
|
||||
var account = await _db.Accounts.FindAsync(accountId)
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
var account = await db.Accounts.FindAsync(accountId)
|
||||
?? throw new InvalidOperationException($"Account {accountId} not found.");
|
||||
account.Name = newName;
|
||||
await _db.SaveChangesAsync();
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateCarryoverAsync(int accountId, decimal carryoverBalance)
|
||||
{
|
||||
var account = await _db.Accounts.FindAsync(accountId)
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
var account = await db.Accounts.FindAsync(accountId)
|
||||
?? throw new InvalidOperationException($"Account {accountId} not found.");
|
||||
account.CarryoverBalance = carryoverBalance;
|
||||
await _db.SaveChangesAsync();
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAccountAsync(int accountId)
|
||||
{
|
||||
var account = await _db.Accounts
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
var account = await db.Accounts
|
||||
.Include(a => a.Entries)
|
||||
.FirstOrDefaultAsync(a => a.Id == accountId)
|
||||
?? throw new InvalidOperationException($"Account {accountId} not found.");
|
||||
|
||||
var entryIds = account.Entries.Select(e => e.Id).ToList();
|
||||
var transferLinks = await _db.TransferLinks
|
||||
var transferLinks = await db.TransferLinks
|
||||
.Where(tl => entryIds.Contains(tl.SourceEntryId) || entryIds.Contains(tl.TargetEntryId))
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var link in transferLinks)
|
||||
{
|
||||
var otherEntryId = entryIds.Contains(link.SourceEntryId) ? link.TargetEntryId : link.SourceEntryId;
|
||||
var otherEntry = await _db.Entries.FindAsync(otherEntryId);
|
||||
var otherEntry = await db.Entries.FindAsync(otherEntryId);
|
||||
if (otherEntry != null)
|
||||
_db.Entries.Remove(otherEntry);
|
||||
db.Entries.Remove(otherEntry);
|
||||
}
|
||||
_db.TransferLinks.RemoveRange(transferLinks);
|
||||
db.TransferLinks.RemoveRange(transferLinks);
|
||||
|
||||
_db.Accounts.Remove(account);
|
||||
await _db.SaveChangesAsync();
|
||||
db.Accounts.Remove(account);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private static AccountSummaryDto MapToSummary(Account account)
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using Duempelkas.App.Services;
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
namespace Duempelkas.Infrastructure.Services;
|
||||
|
||||
public class BackupService : IBackupService
|
||||
{
|
||||
private static readonly string DbPath = Path.Combine(
|
||||
Path.GetDirectoryName(Environment.ProcessPath ?? AppContext.BaseDirectory) ?? AppContext.BaseDirectory,
|
||||
"duempelkas.db");
|
||||
|
||||
public async Task<string> CreateBackupAsync()
|
||||
{
|
||||
if (!File.Exists(DbPath))
|
||||
{
|
||||
return "Keine Datenbankdatei gefunden.";
|
||||
}
|
||||
|
||||
var suggestedFileName = $"duempelkas-backup-{DateTime.Now:yyMMdd-HHmm}.bak";
|
||||
var backupPath = await ShowSaveDialogAsync(suggestedFileName);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(backupPath))
|
||||
{
|
||||
return "Backup wurde abgebrochen.";
|
||||
}
|
||||
|
||||
backupPath = Path.ChangeExtension(backupPath, ".bak");
|
||||
|
||||
try
|
||||
{
|
||||
File.Copy(DbPath, backupPath, overwrite: true);
|
||||
return $"Backup gespeichert: {backupPath}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"Backup fehlgeschlagen: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> RestoreBackupAsync()
|
||||
{
|
||||
var backupPath = await ShowOpenDialogAsync();
|
||||
if (string.IsNullOrWhiteSpace(backupPath))
|
||||
{
|
||||
return "Wiederherstellung wurde abgebrochen.";
|
||||
}
|
||||
|
||||
if (!File.Exists(backupPath))
|
||||
{
|
||||
return "Die gewählte Backup-Datei wurde nicht gefunden.";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Ensure no pooled connection keeps stale handles to the previous DB file.
|
||||
SqliteConnection.ClearAllPools();
|
||||
|
||||
var targetDirectory = Path.GetDirectoryName(DbPath);
|
||||
if (!string.IsNullOrWhiteSpace(targetDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(targetDirectory);
|
||||
}
|
||||
|
||||
File.Copy(backupPath, DbPath, overwrite: true);
|
||||
SqliteConnection.ClearAllPools();
|
||||
return "Wiederherstellung erfolgreich. Die Datenbank wurde neu geladen.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"Wiederherstellung fehlgeschlagen: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private static Task<string?> ShowSaveDialogAsync(string suggestedFileName)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
var documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||||
return Path.Combine(documentsPath, suggestedFileName);
|
||||
}
|
||||
|
||||
var script = $@"
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
$dialog = New-Object System.Windows.Forms.SaveFileDialog
|
||||
$dialog.Filter = 'Backup-Dateien (*.bak)|*.bak'
|
||||
$dialog.DefaultExt = 'bak'
|
||||
$dialog.AddExtension = $true
|
||||
$dialog.OverwritePrompt = $true
|
||||
$dialog.FileName = '{suggestedFileName.Replace("'", "''")}'
|
||||
$dialog.Title = 'Backup speichern'
|
||||
if ($dialog.ShowDialog() -eq 'OK') {{ $dialog.FileName }} else {{ '' }}
|
||||
";
|
||||
|
||||
return RunPowerShellDialog(script);
|
||||
});
|
||||
}
|
||||
|
||||
private static Task<string?> ShowOpenDialogAsync()
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var script = @"
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
$dialog = New-Object System.Windows.Forms.OpenFileDialog
|
||||
$dialog.Filter = 'Backup-Dateien (*.bak)|*.bak'
|
||||
$dialog.Multiselect = $false
|
||||
$dialog.Title = 'Backup auswählen'
|
||||
if ($dialog.ShowDialog() -eq 'OK') { $dialog.FileName } else { '' }
|
||||
";
|
||||
|
||||
return RunPowerShellDialog(script);
|
||||
});
|
||||
}
|
||||
|
||||
private static string? RunPowerShellDialog(string script)
|
||||
{
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = "powershell",
|
||||
Arguments = $"-NoProfile -Command \"{script.Replace("\"", "\\\"") }\"",
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using var process = Process.Start(psi);
|
||||
if (process == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = process.StandardOutput.ReadToEnd().Trim();
|
||||
process.WaitForExit();
|
||||
|
||||
return string.IsNullOrWhiteSpace(result) ? null : result;
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,15 @@ namespace Duempelkas.Infrastructure.Services;
|
||||
|
||||
public class BalanceQueryService : IBalanceQueryService
|
||||
{
|
||||
private readonly FinanceDbContext _db;
|
||||
private readonly IDbContextFactory<FinanceDbContext> _dbFactory;
|
||||
|
||||
public BalanceQueryService(FinanceDbContext db) => _db = db;
|
||||
public BalanceQueryService(IDbContextFactory<FinanceDbContext> dbFactory) => _dbFactory = dbFactory;
|
||||
|
||||
public async Task<AccountBalanceDto> GetAccountBalanceAsync(int accountId)
|
||||
{
|
||||
var account = await _db.Accounts
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
var account = await db.Accounts
|
||||
.Include(a => a.Entries)
|
||||
.FirstOrDefaultAsync(a => a.Id == accountId)
|
||||
?? throw new InvalidOperationException($"Konto {accountId} nicht gefunden.");
|
||||
|
||||
@@ -9,13 +9,15 @@ namespace Duempelkas.Infrastructure.Services;
|
||||
|
||||
public class EntryService : IEntryService
|
||||
{
|
||||
private readonly FinanceDbContext _db;
|
||||
private readonly IDbContextFactory<FinanceDbContext> _dbFactory;
|
||||
|
||||
public EntryService(FinanceDbContext db) => _db = db;
|
||||
public EntryService(IDbContextFactory<FinanceDbContext> dbFactory) => _dbFactory = dbFactory;
|
||||
|
||||
public async Task<List<EntryDto>> GetEntriesAsync(int accountId, bool currentYearOnly)
|
||||
{
|
||||
var query = _db.Entries.Where(e => e.AccountId == accountId);
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
var query = db.Entries.Where(e => e.AccountId == accountId);
|
||||
|
||||
if (currentYearOnly)
|
||||
query = query.Where(e => e.Date.Year == DateTime.Now.Year);
|
||||
@@ -27,7 +29,7 @@ public class EntryService : IEntryService
|
||||
|
||||
var entryIds = entries.Select(e => e.Id).ToList();
|
||||
|
||||
var transferLinks = await _db.TransferLinks
|
||||
var transferLinks = await db.TransferLinks
|
||||
.Include(tl => tl.SourceEntry).ThenInclude(e => e.Account)
|
||||
.Include(tl => tl.TargetEntry).ThenInclude(e => e.Account)
|
||||
.Where(tl => entryIds.Contains(tl.SourceEntryId) || entryIds.Contains(tl.TargetEntryId))
|
||||
@@ -59,7 +61,9 @@ public class EntryService : IEntryService
|
||||
|
||||
public async Task<EntryDto> CreateEntryAsync(int accountId, EntryType type, DateTime date, string title, decimal amount)
|
||||
{
|
||||
var displayId = await GenerateDisplayIdAsync(accountId, date.Year);
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
var displayId = await GenerateDisplayIdAsync(db, accountId, date.Year);
|
||||
|
||||
var entry = new Entry
|
||||
{
|
||||
@@ -70,8 +74,8 @@ public class EntryService : IEntryService
|
||||
Title = title,
|
||||
Amount = amount
|
||||
};
|
||||
_db.Entries.Add(entry);
|
||||
await _db.SaveChangesAsync();
|
||||
db.Entries.Add(entry);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
return new EntryDto(entry.Id, entry.AccountId, entry.DisplayId, entry.Type, entry.Date, entry.Title, entry.Amount, false, false, null, null, null);
|
||||
}
|
||||
@@ -81,9 +85,10 @@ public class EntryService : IEntryService
|
||||
if (sourceAccountId == targetAccountId)
|
||||
throw new InvalidOperationException("Umbuchung innerhalb desselben Kontos ist nicht möglich.");
|
||||
|
||||
await using var transaction = await _db.Database.BeginTransactionAsync();
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
await using var transaction = await db.Database.BeginTransactionAsync();
|
||||
|
||||
var sourceDisplayId = await GenerateDisplayIdAsync(sourceAccountId, date.Year);
|
||||
var sourceDisplayId = await GenerateDisplayIdAsync(db, sourceAccountId, date.Year);
|
||||
|
||||
var sourceEntry = new Entry
|
||||
{
|
||||
@@ -95,10 +100,10 @@ public class EntryService : IEntryService
|
||||
Amount = amount
|
||||
};
|
||||
|
||||
_db.Entries.Add(sourceEntry);
|
||||
await _db.SaveChangesAsync();
|
||||
db.Entries.Add(sourceEntry);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var targetDisplayId = await GenerateDisplayIdAsync(targetAccountId, date.Year);
|
||||
var targetDisplayId = await GenerateDisplayIdAsync(db, targetAccountId, date.Year);
|
||||
|
||||
var targetEntry = new Entry
|
||||
{
|
||||
@@ -110,8 +115,8 @@ public class EntryService : IEntryService
|
||||
Amount = amount
|
||||
};
|
||||
|
||||
_db.Entries.Add(targetEntry);
|
||||
await _db.SaveChangesAsync();
|
||||
db.Entries.Add(targetEntry);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var link = new TransferLink
|
||||
{
|
||||
@@ -119,62 +124,68 @@ public class EntryService : IEntryService
|
||||
TargetEntryId = targetEntry.Id,
|
||||
Note = $"Umbuchung: {title}"
|
||||
};
|
||||
_db.TransferLinks.Add(link);
|
||||
await _db.SaveChangesAsync();
|
||||
db.TransferLinks.Add(link);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
sourceEntry.TransferLinkId = link.Id;
|
||||
targetEntry.TransferLinkId = link.Id;
|
||||
await _db.SaveChangesAsync();
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
await transaction.CommitAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteEntryAsync(int entryId)
|
||||
{
|
||||
var entry = await _db.Entries.FindAsync(entryId)
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
var entry = await db.Entries.FindAsync(entryId)
|
||||
?? throw new InvalidOperationException($"Eintrag {entryId} nicht gefunden.");
|
||||
|
||||
entry.IsDeleted = true;
|
||||
|
||||
var link = await _db.TransferLinks
|
||||
var link = await db.TransferLinks
|
||||
.FirstOrDefaultAsync(tl => tl.SourceEntryId == entryId || tl.TargetEntryId == entryId);
|
||||
|
||||
if (link != null)
|
||||
{
|
||||
var otherEntryId = link.SourceEntryId == entryId ? link.TargetEntryId : link.SourceEntryId;
|
||||
var otherEntry = await _db.Entries.FindAsync(otherEntryId);
|
||||
var otherEntry = await db.Entries.FindAsync(otherEntryId);
|
||||
if (otherEntry != null) otherEntry.IsDeleted = true;
|
||||
}
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task RestoreEntryAsync(int entryId)
|
||||
{
|
||||
var entry = await _db.Entries.FindAsync(entryId)
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
var entry = await db.Entries.FindAsync(entryId)
|
||||
?? throw new InvalidOperationException($"Eintrag {entryId} nicht gefunden.");
|
||||
|
||||
entry.IsDeleted = false;
|
||||
|
||||
var link = await _db.TransferLinks
|
||||
var link = await db.TransferLinks
|
||||
.FirstOrDefaultAsync(tl => tl.SourceEntryId == entryId || tl.TargetEntryId == entryId);
|
||||
|
||||
if (link != null)
|
||||
{
|
||||
var otherEntryId = link.SourceEntryId == entryId ? link.TargetEntryId : link.SourceEntryId;
|
||||
var otherEntry = await _db.Entries.FindAsync(otherEntryId);
|
||||
var otherEntry = await db.Entries.FindAsync(otherEntryId);
|
||||
if (otherEntry != null) otherEntry.IsDeleted = false;
|
||||
}
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateEntryAsync(int entryId, DateTime date, string title, decimal amount)
|
||||
{
|
||||
var entry = await _db.Entries.FindAsync(entryId)
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
var entry = await db.Entries.FindAsync(entryId)
|
||||
?? throw new InvalidOperationException($"Eintrag {entryId} nicht gefunden.");
|
||||
|
||||
var link = await _db.TransferLinks
|
||||
var link = await db.TransferLinks
|
||||
.FirstOrDefaultAsync(tl => tl.SourceEntryId == entryId || tl.TargetEntryId == entryId);
|
||||
|
||||
entry.Date = date;
|
||||
@@ -184,7 +195,7 @@ public class EntryService : IEntryService
|
||||
if (link != null)
|
||||
{
|
||||
var otherEntryId = link.SourceEntryId == entryId ? link.TargetEntryId : link.SourceEntryId;
|
||||
var otherEntry = await _db.Entries.FindAsync(otherEntryId);
|
||||
var otherEntry = await db.Entries.FindAsync(otherEntryId);
|
||||
if (otherEntry != null)
|
||||
{
|
||||
otherEntry.Date = date;
|
||||
@@ -193,20 +204,22 @@ public class EntryService : IEntryService
|
||||
}
|
||||
}
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateTransferAsync(int entryId, int newLinkedAccountId, DateTime date, string title, decimal amount)
|
||||
{
|
||||
var entry = await _db.Entries.FindAsync(entryId)
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
var entry = await db.Entries.FindAsync(entryId)
|
||||
?? throw new InvalidOperationException($"Eintrag {entryId} nicht gefunden.");
|
||||
|
||||
var link = await _db.TransferLinks
|
||||
var link = await db.TransferLinks
|
||||
.FirstOrDefaultAsync(tl => tl.SourceEntryId == entryId || tl.TargetEntryId == entryId)
|
||||
?? throw new InvalidOperationException($"Kein Transfer-Link für Eintrag {entryId} gefunden.");
|
||||
|
||||
var otherEntryId = link.SourceEntryId == entryId ? link.TargetEntryId : link.SourceEntryId;
|
||||
var otherEntry = await _db.Entries.FindAsync(otherEntryId)
|
||||
var otherEntry = await db.Entries.FindAsync(otherEntryId)
|
||||
?? throw new InvalidOperationException($"Gegeneintrag {otherEntryId} nicht gefunden.");
|
||||
|
||||
// Update date, title, amount on both sides
|
||||
@@ -221,16 +234,16 @@ public class EntryService : IEntryService
|
||||
if (otherEntry.AccountId != newLinkedAccountId)
|
||||
{
|
||||
otherEntry.AccountId = newLinkedAccountId;
|
||||
otherEntry.DisplayId = await GenerateDisplayIdAsync(newLinkedAccountId, date.Year);
|
||||
otherEntry.DisplayId = await GenerateDisplayIdAsync(db, newLinkedAccountId, date.Year);
|
||||
}
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task<string> GenerateDisplayIdAsync(int accountId, int year)
|
||||
private static async Task<string> GenerateDisplayIdAsync(FinanceDbContext db, int accountId, int year)
|
||||
{
|
||||
var prefix = $"{year}-";
|
||||
var maxDisplayId = await _db.Entries
|
||||
var maxDisplayId = await db.Entries
|
||||
.Where(e => e.DisplayId.StartsWith(prefix))
|
||||
.Select(e => e.DisplayId)
|
||||
.MaxAsync(id => (string?)id);
|
||||
|
||||
@@ -12,15 +12,15 @@ namespace Duempelkas.Infrastructure.Services;
|
||||
|
||||
public class PdfStatementService : IPdfStatementService
|
||||
{
|
||||
private readonly FinanceDbContext _db;
|
||||
private readonly IDbContextFactory<FinanceDbContext> _dbFactory;
|
||||
private readonly IEntryService _entryService;
|
||||
private readonly IBalanceQueryService _balanceQueryService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private static readonly CultureInfo DeLocale = new("de-DE");
|
||||
|
||||
public PdfStatementService(FinanceDbContext db, IEntryService entryService, IBalanceQueryService balanceQueryService, ISettingsService settingsService)
|
||||
public PdfStatementService(IDbContextFactory<FinanceDbContext> dbFactory, IEntryService entryService, IBalanceQueryService balanceQueryService, ISettingsService settingsService)
|
||||
{
|
||||
_db = db;
|
||||
_dbFactory = dbFactory;
|
||||
_entryService = entryService;
|
||||
_balanceQueryService = balanceQueryService;
|
||||
_settingsService = settingsService;
|
||||
@@ -28,7 +28,9 @@ public class PdfStatementService : IPdfStatementService
|
||||
|
||||
public async Task<byte[]> GenerateStatementAsync(int accountId, bool currentYearOnly)
|
||||
{
|
||||
var account = await _db.Accounts.FindAsync(accountId)
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
|
||||
var account = await db.Accounts.FindAsync(accountId)
|
||||
?? throw new InvalidOperationException($"Konto {accountId} nicht gefunden.");
|
||||
|
||||
var entries = await _entryService.GetEntriesAsync(accountId, currentYearOnly);
|
||||
|
||||
Reference in New Issue
Block a user