Skip to content

Commit

Permalink
Modify Trade Republic PDF-Importer to support new transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
Nirus2000 committed May 21, 2024
1 parent 22b0977 commit 8348888
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
PDFBox Version: 1.8.17
Portfolio Performance Version: 0.68.4
-----------------------------------------
TRADE REPUBLIC BANK GMBH BRUNNENSTRASSE 19-21 10119 BERLIN
cgWgw mLh bolGSUQ PAGE 1 / 1
42 VRwFhq Bg dYWFU DATE 16/05/2024
66838 ageKj COMPTE-TITRES 8041860503
DIVIDENDE
RÉCAPITULATIF
Dividende à la date du 10/05/2024.
POSITION QUANTITÉ REVENU MONTANT
Apple Inc. 1,92086 titre(s) 0,25 USD 0,48 USD
Registered Shares o.N.
ISIN : US0378331005
TOTAL 0,48 USD
DÉTAIL
POSITION MONTANT
Impôt à la source pour les émetteurs américains -0,07 USD
Sous-total 0,41 USD
Sous-total 1,0802 EUR/USD 0,38 EUR
TOTAL 0,38 EUR
RÉSERVATION
COMPTE-ESPÈCES DATE DE VALEUR MONTANT
DE98502109007017811111 16/05/2024 0,38 EUR
Apple Inc. Registered Shares o.N. en garde collective en Allemagne.
Ce document est généré automatiquement et n'est donc pas signé.
Si aucune taxe sur le chiffre d'affaires n'est indiquée, il s'agit d'un service non sujet à la taxe sur le chiffre d'affaires (conformément à la
réglementation UStG. § 4 n° 8).
Trade Republic Bank GmbH www.traderepublic.com Siège de la Société: Berlin Directeur Général
Brunnenstraße 19-21 service@traderepublic.com AG Charlottenburg HRB 244347 B Andreas Torner
10119 Berlin VAT-ID DE307510626 Gernot Mittendorfer
ABRE / 16/05/2024 / 64217923 / d7c6-4ca9
Original file line number Diff line number Diff line change
Expand Up @@ -3745,6 +3745,76 @@ public void testDividende16WithSecurityInEUR()
}))));
}

@Test
public void testDividende17()
{
TradeRepublicPDFExtractor extractor = new TradeRepublicPDFExtractor(new Client());

List<Exception> errors = new ArrayList<>();

List<Item> results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Dividende17.txt"), errors);

assertThat(errors, empty());
assertThat(countSecurities(results), is(1L));
assertThat(countBuySell(results), is(0L));
assertThat(countAccountTransactions(results), is(1L));
assertThat(results.size(), is(2));
new AssertImportActions().check(results, CurrencyUnit.EUR);

// check security
assertThat(results, hasItem(security( //
hasIsin("US0378331005"), hasWkn(null), hasTicker(null), //
hasName("Apple Inc. Registered Shares o.N."), //
hasCurrencyCode("USD"))));

// check dividends transaction
assertThat(results, hasItem(dividend( //
hasDate("2024-05-16T00:00"), hasShares(1.92086), //
hasSource("Dividende17.txt"), //
hasNote(null), //
hasAmount("EUR", 0.38), hasGrossValue("EUR", 0.44), //
hasForexGrossValue("USD", 0.48), //
hasTaxes("EUR", 0.06), hasFees("EUR", 0.00))));
}

@Test
public void testDividende17WithSecurityInEUR()
{
Security security = new Security("Apple Inc. Registered Shares o.N.", CurrencyUnit.EUR);
security.setIsin("US0378331005");

Client client = new Client();
client.addSecurity(security);

TradeRepublicPDFExtractor extractor = new TradeRepublicPDFExtractor(client);

List<Exception> errors = new ArrayList<>();

List<Item> results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Dividende17.txt"), errors);

assertThat(errors, empty());
assertThat(countSecurities(results), is(0L));
assertThat(countBuySell(results), is(0L));
assertThat(countAccountTransactions(results), is(1L));
assertThat(results.size(), is(1));
new AssertImportActions().check(results, CurrencyUnit.EUR);

// check dividends transaction
assertThat(results, hasItem(dividend( //
hasDate("2024-05-16T00:00"), hasShares(1.92086), //
hasSource("Dividende17.txt"), //
hasNote(null), //
hasAmount("EUR", 0.38), hasGrossValue("EUR", 0.44), //
hasTaxes("EUR", 0.06), hasFees("EUR", 0.00), //
check(tx -> {
CheckCurrenciesAction c = new CheckCurrenciesAction();
Account account = new Account();
account.setCurrencyCode(CurrencyUnit.EUR);
Status s = c.process((AccountTransaction) tx, account);
assertThat(s, is(Status.OK_STATUS));
}))));
}

@Test
public void testDividendo01()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -912,12 +912,16 @@ private void addDividendeTransaction()
// Apple Inc. 0.0929 Pcs. 0.24 USD 0.02 USD
// Registered Shares o.N.
// ISIN: US0378331005
//
// Apple Inc. 1,92086 titre(s) 0,25 USD 0,48 USD
// Registered Shares o.N.
// ISIN : US0378331005
// @formatter:on
section -> section //
.attributes("name", "currency", "isin", "nameContinued") //
.match("^(?<name>.*) [\\.,\\d]+ (Stk\\.|Pz\\.|Pcs\\.) [\\.,\\d]+ (?<currency>[\\w]{3}) [\\.,\\d]+ [\\w]{3}$") //
.match("^(?<name>.*) [\\.,\\d]+ (Stk\\.|titre\\(s\\)|Pz\\.|Pcs\\.) [\\.,\\d]+ (?<currency>[\\w]{3}) [\\.,\\d]+ [\\w]{3}$") //
.match("^(?<nameContinued>.*)$") //
.match("^(ISIN: )?(?<isin>[A-Z]{2}[A-Z0-9]{9}[0-9])$") //
.match("^(ISIN([\\s])?:([\\s])?)?(?<isin>[A-Z]{2}[A-Z0-9]{9}[0-9])$") //
.assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))),
// @formatter:off
// 1 Kapitalmaßnahme Barrick Gold Corp. 8,4226 Stk.
Expand Down Expand Up @@ -954,7 +958,7 @@ private void addDividendeTransaction()
// @formatter:on
section -> section //
.attributes("shares") //
.match("^.* (?<shares>[\\.,\\d]+) (Stk\\.|Pz\\.) [\\.,\\d]+ [\\w]{3} [\\.,\\d]+ [\\w]{3}$") //
.match("^.* (?<shares>[\\.,\\d]+) (Stk\\.|titre\\(s\\)|Pz\\.) [\\.,\\d]+ [\\w]{3} [\\.,\\d]+ [\\w]{3}$") //
.assign((t, v) -> t.setShares(asShares(v.get("shares")))),
// @formatter:off
// Apple Inc. 0.0929 Pcs. 0.24 USD 0.02 USD
Expand All @@ -972,12 +976,28 @@ private void addDividendeTransaction()
.match("^[\\d] (Reinvestierung|Kapitalmaßnahme) .* (?<shares>[\\.,\\d]+) Stk\\.$") //
.assign((t, v) -> t.setShares(asShares(v.get("shares")))))

// @formatter:off
// DExxxxxx 25.09.2019 4,18 EUR
// @formatter:on
.section("date") //
.match("^[\\w]+ (?<date>([\\d]{2}\\.[\\d]{2}\\.[\\d]{4}|[\\d]{4}\\-[\\d]{2}\\-[\\d]{2})) (\\-)?[\\.,\\d]+ [\\w]{3}$") //
.assign((t, v) -> t.setDateTime(asDate(v.get("date"))))
.oneOf( //
// @formatter:off
// DExxxxxx 25.09.2019 4,18 EUR
// @formatter:on
section -> section //
.attributes("date") //
.match("^[\\w]+ (?<date>[\\d]{2}\\.[\\d]{2}\\.[\\d]{4}) (\\-)?[\\.,\\d]+ [\\w]{3}$") //
.assign((t, v) -> t.setDateTime(asDate(v.get("date")))),
// @formatter:off
//
// @formatter:on
section -> section //
.attributes("date") //
.match("^[\\w]+ (?<date>[\\d]{4}\\-[\\d]{2}\\-[\\d]{2}) (\\-)?[\\.,\\d]+ [\\w]{3}$") //
.assign((t, v) -> t.setDateTime(asDate(v.get("date")))),
// @formatter:off
// DE98502109007017811111 16/05/2024 0,38 EUR
// @formatter:on
section -> section //
.attributes("date") //
.match("^[\\w]+ (?<date>[\\d]{2}\\/[\\d]{2}\\/[\\d]{4}) (\\-)?[\\.,\\d]+ [\\w]{3}$") //
.assign((t, v) -> t.setDateTime(asDate(v.get("date")))))

.oneOf( //
// @formatter:off
Expand Down Expand Up @@ -1035,11 +1055,14 @@ private void addDividendeTransaction()
//
// TOTAL 0.02 USD
// Subtotal 1.0715 EUR/USD 0.02 EUR
//
// TOTAL 0,48 USD
// Sous-total 1,0802 EUR/USD 0,38 EUR
// @formatter:on
section -> section //
.attributes("fxGross", "exchangeRate", "baseCurrency", "termCurrency") //
.match("^(GESAMT|TOTALE|TOTAL) (\\-)?(?<fxGross>[\\.,\\d]+) [\\w]{3}$") //
.match("^(Zwischensumme|Subtotale|Subtotal) (?<exchangeRate>[\\.,\\d]+) (?<baseCurrency>[\\w]{3})\\/(?<termCurrency>[\\w]{3}) (\\-)?[\\.,\\d]+ [\\w]{3}$") //
.match("^(Zwischensumme|Subtotale|Subtotal|Sous\\-total) (?<exchangeRate>[\\.,\\d]+) (?<baseCurrency>[\\w]{3})\\/(?<termCurrency>[\\w]{3}) (\\-)?[\\.,\\d]+ [\\w]{3}$") //
.assign((t, v) -> {
ExtrExchangeRate rate = asExchangeRate(v);
type.getCurrentContext().putType(rate);
Expand Down Expand Up @@ -2237,12 +2260,16 @@ private void addDividendeTaxReturnBlock(DocumentType type)
// Apple Inc. 0.0929 Pcs. 0.24 USD 0.02 USD
// Registered Shares o.N.
// ISIN: US0378331005
//
// Apple Inc. 1,92086 titre(s) 0,25 USD 0,48 USD
// Registered Shares o.N.
// ISIN : US037833100
// @formatter:on
section -> section //
.attributes("name", "currency", "isin", "nameContinued") //
.match("^(?<name>.*) [\\.,\\d]+ (Stk\\.|Pz\\.|Pcs\\.) [\\.,\\d]+ (?<currency>[\\w]{3}) [\\.,\\d]+ [\\w]{3}$") //
.match("^(?<name>.*) [\\.,\\d]+ (Stk\\.|titre\\(s\\)|Pz\\.|Pcs\\.) [\\.,\\d]+ (?<currency>[\\w]{3}) [\\.,\\d]+ [\\w]{3}$") //
.match("^(?<nameContinued>.*)$") //
.match("^(ISIN: )?(?<isin>[A-Z]{2}[A-Z0-9]{9}[0-9])$") //
.match("^(ISIN([\\s])?:([\\s])?)?(?<isin>[A-Z]{2}[A-Z0-9]{9}[0-9])$") //
.assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))),
// @formatter:off
// 1 Kapitalmaßnahme Barrick Gold Corp. 8,4226 Stk.
Expand Down Expand Up @@ -2276,10 +2303,11 @@ private void addDividendeTaxReturnBlock(DocumentType type)
// iShsV-EM Dividend UCITS ETF 10 Stk. 0,563 USD 5,63 USD
// Enbridge Inc. 20,971565 Pz. 0,8875 CAD 18,61 CAD
// Apple Inc. 0.0929 Pcs. 0.24 USD 0.02 USD
// Apple Inc. 1,92086 titre(s) 0,25 USD 0,48 USD
// @formatter:on
section -> section //
.attributes("shares") //
.match("^.* (?<shares>[\\.,\\d]+) (Stk\\.|Pz\\.) [\\.,\\d]+ [\\w]{3} [\\.,\\d]+ [\\w]{3}$") //
.match("^.* (?<shares>[\\.,\\d]+) (Stk\\.|titre\\(s\\)|Pz\\.) [\\.,\\d]+ [\\w]{3} [\\.,\\d]+ [\\w]{3}$") //
.assign((t, v) -> t.setShares(asShares(v.get("shares")))),
// @formatter:off
// Apple Inc. 0.0929 Pcs. 0.24 USD 0.02 USD
Expand All @@ -2297,18 +2325,37 @@ private void addDividendeTaxReturnBlock(DocumentType type)
.match("^[\\d] (Reinvestierung|Kapitalmaßnahme) .* (?<shares>[\\.,\\d]+) Stk\\.$") //
.assign((t, v) -> t.setShares(asShares(v.get("shares")))))

// @formatter:off
// DExxxxxx 25.09.2019 4,18 EUR
// @formatter:on
.section("date") //
.match("^[\\w]+ (?<date>([\\d]{2}\\.[\\d]{2}\\.[\\d]{4}|[\\d]{4}\\-[\\d]{2}\\-[\\d]{2})) (\\-)?[\\.,\\d]+ [\\w]{3}$") //
.assign((t, v) -> t.setDateTime(asDate(v.get("date"))))
.oneOf( //
// @formatter:off
// DExxxxxx 25.09.2019 4,18 EUR
// @formatter:on
section -> section //
.attributes("date") //
.match("^[\\w]+ (?<date>[\\d]{2}\\.[\\d]{2}\\.[\\d]{4}) (\\-)?[\\.,\\d]+ [\\w]{3}$") //
.assign((t, v) -> t.setDateTime(asDate(v.get("date")))),
// @formatter:off
//
// @formatter:on
section -> section //
.attributes("date") //
.match("^[\\w]+ (?<date>[\\d]{4}\\-[\\d]{2}\\-[\\d]{2}) (\\-)?[\\.,\\d]+ [\\w]{3}$") //
.assign((t, v) -> t.setDateTime(asDate(v.get("date")))),
// @formatter:off
// DE98502109007017811111 16/05/2024 0,38 EUR
// @formatter:on
section -> section //
.attributes("date") //
.match("^[\\w]+ (?<date>[\\d]{2}\\/[\\d]{2}\\/[\\d]{4}) (\\-)?[\\.,\\d]+ [\\w]{3}$") //
.assign((t, v) -> t.setDateTime(asDate(v.get("date")))))

// @formatter:off
// DE99012345670123456789 10.01.2024 68,74 EUR
// DE98502109007017811111 16/05/2024 0,38 EUR
// @formatter:on
.section("currency") //
.match("^[\\w]+ (?<date>([\\d]{2}\\.[\\d]{2}\\.[\\d]{4}|[\\d]{4}\\-[\\d]{2}\\-[\\d]{2})) (\\-)?[\\.,\\d]+ (?<currency>[\\w]{3})$") //
.match("^[\\w]+ (?<date>([\\d]{2}\\.[\\d]{2}\\.[\\d]{4}"
+ "|[\\d]{2}\\/[\\d]{2}\\/[\\d]{4}"
+ "|[\\d]{4}\\-[\\d]{2}\\-[\\d]{2})) (\\-)?[\\.,\\d]+ (?<currency>[\\w]{3})$") //
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(0L);
Expand Down Expand Up @@ -2346,9 +2393,10 @@ private void addDividendeTaxReturnBlock(DocumentType type)

// @formatter:off
// Zwischensumme 1,095514 EUR/USD 63,31 EUR
// Sous-total 1,0802 EUR/USD 0,38 EUR
// @formatter:on
.section("exchangeRate", "baseCurrency", "termCurrency").optional() //
.match("^(Zwischensumme|Subtotale|Subtotal) (?<exchangeRate>[\\.,\\d]+) (?<baseCurrency>[\\w]{3})\\/(?<termCurrency>[\\w]{3}) (\\-)?[\\.,\\d]+ [\\w]{3}$")
.match("^(Zwischensumme|Subtotale|Subtotal|Sous\\-total) (?<exchangeRate>[\\.,\\d]+) (?<baseCurrency>[\\w]{3})\\/(?<termCurrency>[\\w]{3}) (\\-)?[\\.,\\d]+ [\\w]{3}$")
.assign((t, v) -> {
if (t.getCurrencyCode() != null && t.getAmount() != 0)
{
Expand Down Expand Up @@ -2385,6 +2433,13 @@ private <T extends Transaction<?>> void addTaxesSectionsTransaction(T transactio
.match("^([\\d] )?Quellensteuer .* (\\-)?(?<withHoldingTax>[\\.,\\d]+) (?<currency>[\\w]{3})$") //
.assign((t, v) -> processWithHoldingTaxEntries(t, v, "withHoldingTax", type))

// @formatter:off
// Impôt à la source pour les émetteurs américains -0,07 USD
// @formatter:on
.section("withHoldingTax", "currency").optional() //
.match("^Imp.t . la source pour les .metteurs am.ricains (\\-)?(?<withHoldingTax>[\\.,\\d]+) (?<currency>[\\w]{3})$") //
.assign((t, v) -> processWithHoldingTaxEntries(t, v, "withHoldingTax", type))

// @formatter:off
// Ritenuta alla fonte -4,65 CAD
// @formatter:on
Expand Down

0 comments on commit 8348888

Please sign in to comment.