fix(pdf): align active entries periods and balances across exports (U03)
This commit is contained in:
@@ -95,6 +95,62 @@ public class DashboardPdfStatementServiceTests : IDisposable
|
||||
System.Text.Encoding.ASCII.GetString(pdf.Take(4).ToArray()).Should().Be("%PDF");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Exports_ExcludeDeleted_ExplainTransfers_AndMatchBalances()
|
||||
{
|
||||
var source = new Account { Name = "Barkasse", CarryoverBalance = 100m };
|
||||
var target = new Account { Name = "Girokonto" };
|
||||
_db.Accounts.AddRange(source, target);
|
||||
await _db.SaveChangesAsync();
|
||||
var options = new DbContextOptionsBuilder<FinanceDbContext>().UseSqlite(_connectionString).Options;
|
||||
var factory = new TestDbContextFactory(options);
|
||||
var entries = new EntryService(factory);
|
||||
await entries.CreateEntryAsync(source.Id, EntryType.Income, new(2026, 1, 1), "Mitgliedsbeitrag", 12.50m);
|
||||
await entries.CreateEntryAsync(source.Id, EntryType.Expense, new(2026, 1, 2), "Porto", 4.20m);
|
||||
await entries.CreateTransferAsync(source.Id, target.Id, new(2026, 1, 3), "Zur Bank", 50m);
|
||||
var deleted = await entries.CreateEntryAsync(source.Id, EntryType.Income, new(2026, 1, 4), "GELOESCHTE_TESTBUCHUNG", 999m);
|
||||
await entries.DeleteEntryAsync(deleted.Id);
|
||||
await entries.CreateEntryAsync(source.Id, EntryType.Income, new(2027, 1, 1), "ZUKUNFT_TESTBUCHUNG", 1m);
|
||||
var balance = await new BalanceQueryService(factory).GetAccountBalanceAsync(source.Id, 2026);
|
||||
Assert.Equal(58.30m, balance.TotalBalance);
|
||||
var single = await _pdfService.GenerateStatementAsync(source.Id, 2026);
|
||||
var combined = await _pdfService.GenerateDashboardStatementAsync(2026);
|
||||
foreach (var pdf in new[] { single, combined })
|
||||
{
|
||||
var text = Extract(pdf);
|
||||
Assert.Contains("Mitgliedsbeitrag", text);
|
||||
Assert.Contains("Porto", text);
|
||||
Assert.Contains("Zur Bank", text);
|
||||
Assert.DoesNotContain("GELOESCHTE_TESTBUCHUNG", text);
|
||||
Assert.DoesNotContain("ZUKUNFT_TESTBUCHUNG", text);
|
||||
Assert.Contains("58,30", text);
|
||||
Assert.Contains("31.12.2026", text);
|
||||
}
|
||||
Assert.Contains("108,30", Extract(combined));
|
||||
Assert.Contains("an Girokonto", Extract(single));
|
||||
var all = Extract(await _pdfService.GenerateStatementAsync(source.Id, (int?)null));
|
||||
Assert.Contains("Alle Jahre", all);
|
||||
Assert.Contains("Anfangsbestand", all);
|
||||
Assert.Contains("59,30", all);
|
||||
var evidence = Environment.GetEnvironmentVariable("DUEMPELKAS_PDF_EVIDENCE");
|
||||
if (evidence != null)
|
||||
{
|
||||
Directory.CreateDirectory(evidence);
|
||||
await File.WriteAllBytesAsync(Path.Combine(evidence, "single.pdf"), single);
|
||||
await File.WriteAllBytesAsync(Path.Combine(evidence, "combined.pdf"), combined);
|
||||
// Additional pages exercise repeated headers and the final summary.
|
||||
for (var i = 0; i < 90; i++)
|
||||
await entries.CreateEntryAsync(source.Id, EntryType.Income, new(2026, 2, 1), $"Lesbarkeitsprüfung Buchung {i + 1}", 1m);
|
||||
await File.WriteAllBytesAsync(Path.Combine(evidence, "multipage.pdf"), await _pdfService.GenerateStatementAsync(source.Id, 2026));
|
||||
}
|
||||
}
|
||||
|
||||
private static string Extract(byte[] pdf)
|
||||
{
|
||||
using var document = UglyToad.PdfPig.PdfDocument.Open(pdf);
|
||||
return string.Join("\n", document.GetPages().Select(page => UglyToad.PdfPig.DocumentLayoutAnalysis.TextExtractor.ContentOrderTextExtractor.GetText(page)));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Database.CloseConnection();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.11.0" />
|
||||
<PackageReference Include="PdfPig" Version="0.1.16" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
Reference in New Issue
Block a user