Skip to content

Commit 5e60da3

Browse files
committed
added test for new type of PDF containing a trailing page with
tax-informations leading to parse error. Fixed parse error
1 parent 5822226 commit 5e60da3

4 files changed

Lines changed: 172 additions & 22 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
PDFBox Version: 3.0.6
2+
Portfolio Performance Version: 0.83.2
3+
System: win32 | x86_64 | 21.0.5+11-LTS | Azul Systems, Inc.
4+
-----------------------------------------
5+
TRADE REPUBLIC BANK GMBH BRUNNENSTRASSE 19-21 10119 BERLIN
6+
xxxxxx SEITE 1 von 2
7+
xxxxx DATUM 22.05.2026
8+
xxxxx DEPOT xxxxxxxx
9+
DIVIDENDE
10+
ÜBERSICHT
11+
Dividende mit Ex-Datum 18.05.2026.
12+
POSITION ANZAHL ERTRAG BETRAG
13+
Global Quality Dividends USD (Dist) 2.25 USD
14+
63.726878 Stücke 0.0353 USD
15+
IE0005AJA0P1
16+
GESAMT 2.25 USD
17+
ABRECHNUNG
18+
POSITION BETRAG
19+
Zwischensumme 2.25 USD
20+
Zwischensumme 1.1599 USD/EUR 1.94 EUR
21+
Kapitalertragsteuer 1.1599 USD/EUR -0.34 EUR
22+
Solidaritätszuschlag 1.1599 USD/EUR -0.02 EUR
23+
GESAMT 1.58 EUR
24+
BUCHUNG
25+
VERRECHNUNGSKONTO DATUM DER ZAHLUNG BETRAG
26+
xxxxxxxxxxxxxxx 22.05.2026 1.58 EUR
27+
IE0005AJA0P1 in Wertpapierrechnung
28+
Diese Abrechnung wird maschinell erstellt und daher nicht unterschrieben.
29+
Wird keine Umsatzsteuer ausgewiesen, handelt es sich um eine umsatzsteuerfreie Leistung gemäß § 4 Nr. 8 UStG
30+
Trade Republic Bank GmbH www.traderepublic.com Sitz der Gesellschaft: Berlin Geschäftsführer
31+
Brunnenstraße 19-21 AG Charlottenburg HRB 244347 B Andreas Torner
32+
10119 Berlin Umsatzsteuer-ID DE307510626 Gernot Mittendorfer
33+
Christian Hecker
34+
Thomas Pischke
35+
TRADE REPUBLIC BANK GMBH BRUNNENSTRASSE 19-21 10119 BERLIN
36+
xxxxxxx SEITE 2 von 2
37+
xxxxxxx DATUM 22.05.2026
38+
xxxxxxx DEPOT xxxxxxxx
39+
STEUERLICHE BEHANDLUNG
40+
BERECHNUNG DER STEUERBEMESSUNGSGRUNDLAGE BETRAG
41+
Kapitalertrag 2,25 USD
42+
Zwischensumme 1.1599 EUR/USD 1,94 EUR
43+
Teilfreistellung -0,59 EUR
44+
STEUERBEMESSUNGSGRUNDLAGE 1,35 EUR
45+
BERECHNUNG DER STEUERN BETRAG
46+
Steuerbemessungsgrundlage 1,35 EUR
47+
Kapitalertragsteuer -0,34 EUR
48+
Solidaritätszuschlag -0,02 EUR
49+
GESAMTE STEUERN 0,36 EUR
50+
VERRECHNUNGSTÖPFE
51+
VERRECHNUNGSTÖPFE VORHER VERÄNDERUNG NACHHER
52+
Verlustverrechnungstopf Aktien 0,00 EUR 0,00 EUR 0,00 EUR
53+
Verlustverrechnungstopf Allgemein 0,00 EUR 0,00 EUR 0,00 EUR
54+
Freistellungsauftrag 0,00 EUR 0,00 EUR 0,00 EUR
55+
Quellensteuertopf 0,00 EUR 0,00 EUR 0,00 EUR
56+
Trade Republic Bank GmbH www.traderepublic.com Sitz der Gesellschaft: Berlin Geschäftsführer
57+
Brunnenstraße 19-21 AG Charlottenburg HRB 244347 B Andreas Torner
58+
10119 Berlin Umsatzsteuer-ID DE307510626 Gernot Mittendorfer
59+
Christian Hecker
60+
Thomas Pischke

name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/traderepublic/TradeRepublicPDFExtractorTest.java

Lines changed: 90 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
@SuppressWarnings("nls")
7777
public class TradeRepublicPDFExtractorTest
7878
{
79-
TradeRepublicPDFExtractor extractor = new TradeRepublicPDFExtractor(new Client())
79+
TradeRepublicPDFExtractor cryptExtractor = new TradeRepublicPDFExtractor(new Client())
8080
{
8181
@Override
8282
protected List<SecuritySearchProvider> lookupCryptoProvider()
@@ -1136,7 +1136,7 @@ public void testCryptoKauf01()
11361136
{
11371137
List<Exception> errors = new ArrayList<>();
11381138

1139-
var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf01.txt"), errors);
1139+
var results = cryptExtractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf01.txt"), errors);
11401140

11411141
assertThat(errors, empty());
11421142
assertThat(countSecurities(results), is(1L));
@@ -1170,7 +1170,7 @@ public void testCryptoKauf02()
11701170
{
11711171
List<Exception> errors = new ArrayList<>();
11721172

1173-
var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf02.txt"), errors);
1173+
var results = cryptExtractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf02.txt"), errors);
11741174

11751175
assertThat(errors, empty());
11761176
assertThat(countSecurities(results), is(1L));
@@ -1204,7 +1204,7 @@ public void testCryptoKauf03()
12041204
{
12051205
List<Exception> errors = new ArrayList<>();
12061206

1207-
var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf03.txt"), errors);
1207+
var results = cryptExtractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf03.txt"), errors);
12081208

12091209
assertThat(errors, empty());
12101210
assertThat(countSecurities(results), is(1L));
@@ -1238,7 +1238,7 @@ public void testCryptoKauf04()
12381238
{
12391239
List<Exception> errors = new ArrayList<>();
12401240

1241-
var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf04.txt"), errors);
1241+
var results = cryptExtractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf04.txt"), errors);
12421242

12431243
assertThat(errors, empty());
12441244
assertThat(countSecurities(results), is(1L));
@@ -1272,7 +1272,7 @@ public void testCryptoKauf05()
12721272
{
12731273
List<Exception> errors = new ArrayList<>();
12741274

1275-
var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf05.txt"), errors);
1275+
var results = cryptExtractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf05.txt"), errors);
12761276

12771277
assertThat(errors, empty());
12781278
assertThat(countSecurities(results), is(1L));
@@ -1306,7 +1306,7 @@ public void testCryptoKauf06()
13061306
{
13071307
List<Exception> errors = new ArrayList<>();
13081308

1309-
var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf06.txt"), errors);
1309+
var results = cryptExtractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf06.txt"), errors);
13101310

13111311
assertThat(errors, empty());
13121312
assertThat(countSecurities(results), is(1L));
@@ -1340,7 +1340,7 @@ public void testCryptoKauf07()
13401340
{
13411341
List<Exception> errors = new ArrayList<>();
13421342

1343-
var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf07.txt"), errors);
1343+
var results = cryptExtractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf07.txt"), errors);
13441344

13451345
assertThat(errors, empty());
13461346
assertThat(countSecurities(results), is(1L));
@@ -1374,7 +1374,7 @@ public void testCryptoKauf08()
13741374
{
13751375
List<Exception> errors = new ArrayList<>();
13761376

1377-
var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf08.txt"), errors);
1377+
var results = cryptExtractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf08.txt"), errors);
13781378

13791379
assertThat(errors, empty());
13801380
assertThat(countSecurities(results), is(1L));
@@ -1408,7 +1408,7 @@ public void testCryptoKauf09()
14081408
{
14091409
List<Exception> errors = new ArrayList<>();
14101410

1411-
var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf09.txt"), errors);
1411+
var results = cryptExtractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoKauf09.txt"), errors);
14121412

14131413
assertThat(errors, empty());
14141414
assertThat(countSecurities(results), is(1L));
@@ -1442,7 +1442,7 @@ public void testCryptoVerkauf01()
14421442
{
14431443
List<Exception> errors = new ArrayList<>();
14441444

1445-
var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoVerkauf01.txt"), errors);
1445+
var results = cryptExtractor.extract(PDFInputFile.loadTestCase(getClass(), "CryptoVerkauf01.txt"), errors);
14461446

14471447
assertThat(errors, empty());
14481448
assertThat(countSecurities(results), is(1L));
@@ -8920,6 +8920,85 @@ public void testDividende29WithSecurityInEUR()
89208920
hasTaxes("EUR", 0.01), hasFees("EUR", 0.00))));
89218921
}
89228922

8923+
@Test
8924+
public void testDividende30()
8925+
{
8926+
var extractor = new TradeRepublicPDFExtractor(new Client());
8927+
8928+
List<Exception> errors = new ArrayList<>();
8929+
8930+
var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Dividende30.txt"), errors);
8931+
8932+
errors.forEach(Exception::printStackTrace);
8933+
assertThat(errors, empty());
8934+
assertThat(countSecurities(results), is(1L));
8935+
assertThat(countBuySell(results), is(0L));
8936+
assertThat(countAccountTransactions(results), is(1L));
8937+
assertThat(countAccountTransfers(results), is(0L));
8938+
assertThat(countItemsWithFailureMessage(results), is(0L));
8939+
assertThat(countSkippedItems(results), is(0L));
8940+
assertThat(results.size(), is(2));
8941+
new AssertImportActions().check(results, "EUR");
8942+
8943+
// check security
8944+
assertThat(results, hasItem(security( //
8945+
hasIsin("IE0005AJA0P1"), hasWkn(null), hasTicker(null), //
8946+
hasName("Global Quality Dividends USD (Dist)"), //
8947+
hasCurrencyCode("USD"))));
8948+
8949+
// check dividends transaction
8950+
assertThat(results, hasItem(dividend( //
8951+
hasDate("2026-05-22T00:00"),
8952+
// hasExDate("2025-05-18T00:00"), //
8953+
hasShares(63.726878), //
8954+
hasSource("Dividende30.txt"), //
8955+
hasNote(null), //
8956+
hasAmount("EUR", 1.58), //
8957+
hasGrossValue("EUR", 1.94), //
8958+
hasForexGrossValue("USD", 2.25), //
8959+
hasTaxes("EUR", 0.36), //
8960+
hasFees("EUR", 0.00))));
8961+
}
8962+
8963+
@Test
8964+
public void testDividende30WithSecurityInEUR()
8965+
{
8966+
var security = new Security("Global Quality Dividends USD (Dist)", "EUR");
8967+
security.setIsin("IE0005AJA0P1");
8968+
8969+
var client = new Client();
8970+
client.addSecurity(security);
8971+
8972+
var extractor = new TradeRepublicPDFExtractor(client);
8973+
8974+
List<Exception> errors = new ArrayList<>();
8975+
8976+
var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Dividende30.txt"), errors);
8977+
8978+
errors.forEach(Exception::printStackTrace);
8979+
assertThat(errors, empty());
8980+
assertThat(countSecurities(results), is(0L));
8981+
assertThat(countBuySell(results), is(0L));
8982+
assertThat(countAccountTransactions(results), is(1L));
8983+
assertThat(countAccountTransfers(results), is(0L));
8984+
assertThat(countItemsWithFailureMessage(results), is(0L));
8985+
assertThat(countSkippedItems(results), is(0L));
8986+
assertThat(results.size(), is(1));
8987+
new AssertImportActions().check(results, "EUR");
8988+
8989+
// check dividends transaction
8990+
assertThat(results, hasItem(dividend( //
8991+
hasDate("2026-05-22T00:00"),
8992+
// hasExDate("2025-05-18T00:00"), //
8993+
hasShares(63.726878), //
8994+
hasSource("Dividende30.txt"), //
8995+
hasNote(null), //
8996+
hasAmount("EUR", 1.58), //
8997+
hasGrossValue("EUR", 1.94), //
8998+
hasTaxes("EUR", 0.36), //
8999+
hasFees("EUR", 0.00))));
9000+
}
9001+
89239002
@Test
89249003
public void testDividend01()
89259004
{

name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/PDFParser.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,12 +253,8 @@ public List<LineSpan> split(String[] lines)
253253
endLine = findBlockEnd(lines, startLine, endLine);
254254
if (endLine == -1)
255255
continue;
256-
spans.add(new LineSpan(startLine, endLine));
257-
}
258-
else
259-
{
260-
spans.add(new LineSpan(startLine, endLine));
261256
}
257+
spans.add(new LineSpan(startLine, endLine));
262258
}
263259

264260
return spans;

name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/TradeRepublicPDFExtractor.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,13 +1240,18 @@ private void addBuySellCryptoTransaction()
12401240
addFeesSectionsTransaction(pdfTransaction, type);
12411241
}
12421242

1243+
private static final String BLOCK_ENDSWITH = "^(Diese Abrechnung wird maschinell erstellt und daher nicht unterschrieben\\." //
1244+
+ "|Ce document est .*"
1245+
+ "|Questo regolamento viene creato automaticamente e quindi non . firmato\\."
1246+
+ "|This statement is generated automatically and therefore not signed\\.)$";
1247+
12431248
private void addDividendTransaction()
12441249
{
1245-
final var type = new DocumentType("(AUSSCH.TTUNG" //
1246-
+ "|DIVIDENDE" //
1250+
final var type = new DocumentType("(?i)(AUSSCH.TTUNG" //
1251+
+ "|(STORNIERUNG DER )?DIVIDENDE( EN ESP.CES)?" //
12471252
+ "|REINVESTIERUNG" //
12481253
+ "|STORNO DIVIDENDE" //
1249-
+ "|DIVIDEND" //
1254+
+ "|(CASH )?DIVIDEND" //
12501255
+ "|DIVIDENDO" //
12511256
+ "|DISTRIBUZIONE" //
12521257
+ "|Distribution" //
@@ -1256,7 +1261,16 @@ private void addDividendTransaction()
12561261

12571262
var pdfTransaction = new Transaction<AccountTransaction>();
12581263

1259-
var firstRelevantLine = new Block(); //
1264+
var firstRelevantLine = new Block("(?i)(AUSSCH.TTUNG" //
1265+
+ "|(STORNIERUNG DER )?DIVIDENDE( EN ESP.CES)?" //
1266+
+ "|REINVESTIERUNG" //
1267+
+ "|STORNO DIVIDENDE" //
1268+
+ "|(CASH )?DIVIDEND" //
1269+
+ "|DIVIDENDO" //
1270+
+ "|DISTRIBUZIONE" //
1271+
+ "|Distribution" //
1272+
+ "|KAPITALREDUKTION)", //
1273+
BLOCK_ENDSWITH); //
12601274
type.addBlock(firstRelevantLine);
12611275
firstRelevantLine.set(pdfTransaction);
12621276

@@ -1499,7 +1513,7 @@ private void addDividendTransaction()
14991513
// Dividendo con l'ex-tag 12.05.2023.
15001514
// Distribuzione con l'ex-tag 17.08.2023.
15011515
// Ausschüttung mit dem Ex-Tag 12.09.2019.
1502-
// Dividende mit Ex-Datum22.05.2024.
1516+
// Dividende mit Ex-Datum 22.05.2024.
15031517
// @formatter:on
15041518
section -> section //
15051519
.attributes("exDate") //
@@ -4492,7 +4506,8 @@ private void addDividendeTaxReturnBlock(DocumentType type)
44924506
{
44934507
var pdfTransaction = new Transaction<AccountTransaction>();
44944508

4495-
var firstRelevantLine = new Block("^TRADE REPUBLIC BANK GMBH.*$");
4509+
var firstRelevantLine = new Block("^TRADE REPUBLIC BANK GMBH.*$", //
4510+
BLOCK_ENDSWITH);
44964511
type.addBlock(firstRelevantLine);
44974512
firstRelevantLine.set(pdfTransaction);
44984513

0 commit comments

Comments
 (0)