diff --git a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/fintechgroupbank/FinTechGroupBankPDFExtractorTest.java b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/fintechgroupbank/FinTechGroupBankPDFExtractorTest.java index d51b3ff314..0eb5a4f1ec 100644 --- a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/fintechgroupbank/FinTechGroupBankPDFExtractorTest.java +++ b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/fintechgroupbank/FinTechGroupBankPDFExtractorTest.java @@ -1,14 +1,5 @@ package name.abuchen.portfolio.datatransfer.pdf.fintechgroupbank; -import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countAccountTransactions; -import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countBuySell; -import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countSecurities; -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.collection.IsEmptyCollection.empty; -import static org.junit.Assert.assertNull; - import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.check; import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.deposit; import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.dividend; @@ -35,6 +26,14 @@ import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.security; import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.taxes; import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.withFailureMessage; +import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countAccountTransactions; +import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countBuySell; +import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countSecurities; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsEmptyCollection.empty; +import static org.junit.Assert.assertNull; import java.time.LocalDateTime; import java.util.ArrayList; @@ -1889,7 +1888,7 @@ public void testFinTechDividende06() assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS)); assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2016-04-28T00:00"))); - assertThat(transaction.getShares(), is(Values.Share.factorize(1000))); + assertThat(transaction.getShares(), is(Values.Share.factorize(10))); assertThat(transaction.getSource(), is("FinTechDividende06.txt")); assertThat(transaction.getNote(), is("Transaktion-Nr.: 111111111")); @@ -4750,6 +4749,77 @@ public void testFlatExDegiroDividende09() hasTaxes("CHF", 42.78), hasFees("CHF", 0.00)))); } + @Test + public void testFlatExDegiroDividende10() + { + FinTechGroupBankPDFExtractor extractor = new FinTechGroupBankPDFExtractor(new Client()); + + List errors = new ArrayList<>(); + + List results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "FlatExDegiroDividende10.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("US89114QCB23"), hasWkn("A2RY26"), hasTicker(null), // + hasName("TORON.DOM.BK 19/24 MTN"), // + hasCurrencyCode("USD")))); + + // check taxes transaction + assertThat(results, hasItem(dividend( // + hasDate("2023-09-11T00:00"), hasShares(100.00), // + hasSource("FlatExDegiroDividende10.txt"), // + hasNote("Transaktion-Nr.: 3415691892"), // + hasAmount("EUR", 109.86), hasGrossValue("EUR", 151.53), // + hasForexGrossValue("USD", 162.50), // + hasTaxes("EUR", 41.67), hasFees("EUR", 0.00)))); + } + + @Test + public void testFlatExDegiroDividende10WithSecurityInEUR() + { + Security security = new Security("TORON.DOM.BK 19/24 MTN", CurrencyUnit.EUR); + security.setIsin("US89114QCB23"); + security.setWkn("A2RY26"); + + Client client = new Client(); + client.addSecurity(security); + + FinTechGroupBankPDFExtractor extractor = new FinTechGroupBankPDFExtractor(client); + + List errors = new ArrayList<>(); + + List results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "FlatExDegiroDividende10.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 taxes transaction + assertThat(results, hasItem(dividend( // + hasDate("2023-09-11T00:00"), hasShares(100.00), // + hasSource("FlatExDegiroDividende10.txt"), // + hasNote("Transaktion-Nr.: 3415691892"), // + hasAmount("EUR", 109.86), hasGrossValue("EUR", 151.53), // + hasTaxes("EUR", 41.67), 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 testFlatExSammelabrechnung01() { diff --git a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/fintechgroupbank/FlatExDegiroDividende10.txt b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/fintechgroupbank/FlatExDegiroDividende10.txt new file mode 100644 index 0000000000..6710085f61 --- /dev/null +++ b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/fintechgroupbank/FlatExDegiroDividende10.txt @@ -0,0 +1,41 @@ +PDFBox Version: 1.8.17 +Portfolio Performance Version: 0.65.4 +----------------------------------------- +flatexDEGIRO Bank AG +Kundenservice +Opernring 1 / Top 736 +1010 Wien +Tel.: +43 - (0)720 518 777 +Mail: info@flatex.at +flatexDEGIRO Bank AG - Opernring 1 / Top 736 - A-1010 Wien +0102492000003433116450 +Herrn + + Wien, 12.09.2023 +Zinsgutschrift für Wertpapiere +Ihre Depotnummer: 31010852643 +Depotinhaber : +Nr.3415691892 TORON.DOM.BK 19/24 MTN (US89114QCB23/A2RY26) +St./Nominale : 10.000,00 USD +Zinstage : 180 Ertrag p.a. : 3,2500 % +Extag : 11.09.2023 Ertrag : 1,6250 % +Zinstermin : 11.09.2023 Zinsbetrag : 162,50 USD +Zahlungstag : 11.09.2023 Faktor : 1,000000 +Valuta : 11.09.2023 *Einbeh. Steuer : 41,67 EUR + Devisenkurs : 1,072400 + Endbetrag : 109,86 EUR +* Einbehaltene Kapitalertragsteuer. Details dazu finden Sie im Steuerreport + unter der Transaktion-Nr.: 3415691892. +Die Verrechnung des Endbetrag erfolgt über Ihr Konto Nr.: 31010852635. +Die Gutschrift erfolgt unter Vorbehalt des Eingangs. Einwendungen müssen +unverzüglich nach Zugang erhoben werden. Die Unterlassung rechtzeitiger +Einwendung gilt als Genehmigung. +Dieser Beleg ist keine Steuerbescheinigung. Kapitalerträge sind +einkommensteuerpflichtig. +Diese Mitteilung ist maschinell erstellt und wird nicht unterschrieben. +Für weitergehende Fragen wenden Sie sich bitte an Ihr flatex-Service-Team. +flatexDEGIRO Bank AG Niederlassung Österreich | Aktiengesellschaft | Opernring 1 / Top 736 | 1010 Wien | Handelsgericht Wien | FN 334642 x | DVR 4000544 | UID ATU 65140956 +Hauptniederlassung: flatexDEGIRO Bank AG | Omniturm | Große Gallusstr. 16-18 | 60312 Frankfurt am Main (Deutschland) | Amtsgericht Frankfurt am Main | HRB 105687 +- Vorsitzender des Aufsichtsrats: Martin Korbmacher | Vorstand: Frank Niehage (Vorsitzender), Dr. Benon Janos, +Dr. Matthias Heinrich, Steffen Jentsch, Stephan Simmang - +2000003433116450 0351102490000101 \ No newline at end of file diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/FinTechGroupBankPDFExtractor.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/FinTechGroupBankPDFExtractor.java index 6cf303fb70..0ddbc3ed29 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/FinTechGroupBankPDFExtractor.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/FinTechGroupBankPDFExtractor.java @@ -712,278 +712,230 @@ private void addDividendeTransaction() DocumentType type = new DocumentType("(Dividendengutschrift|Ertragsmitteilung|Zinsgutschrift)"); this.addDocumentTyp(type); - Block block = new Block("^(Dividendengutschrift|Ertragsmitteilung|Zinsgutschrift).*$"); - type.addBlock(block); - Transaction pdfTransaction = new Transaction().subject(() -> { - AccountTransaction entry = new AccountTransaction(); - entry.setType(AccountTransaction.Type.DIVIDENDS); - return entry; - }); - - pdfTransaction - .oneOf( - // @formatter:off - // Nr.716759781 HANN.RUECK SE NA O.N. (DE0008402215/840221) - // Extag : 08.05.2014 Bruttodividende - // Zahlungstag : 08.05.2014 pro Stück : 3,0000 EUR - // @formatter:on - section -> section - .attributes("name", "isin", "wkn", "currency") - .match("^Nr\\.[\\d]+([\\s]+)? (?.*)([\\s]+)? \\((?[A-Z]{2}[A-Z0-9]{9}[0-9])\\/(?[A-Z0-9]{6})\\).*$") - .find(".* (Bruttodividende|Bruttoaussch.ttung|Bruttothesaurierung)") - .match("^.* pro St.ck([:\\s]+)? [\\.,\\d]+ (?[\\w]{3})$") - .assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))) - , - // @formatter:off - // Nr.0123456789 X(IE)-MSCI WRLD MOM. 1CDL (IE00BL25JP72/A1103G) - // St. : 248,34 Bruttothesaurierung - // pro Stück : -0,1323 USD - // @formatter:on - section -> section - .attributes("name", "isin", "wkn", "currency") - .match("^Nr\\.[\\d]+([\\s]+)? (?.*)([\\s]+)? \\((?[A-Z]{2}[A-Z0-9]{9}[0-9])\\/(?[A-Z0-9]{6})\\).*$") - .find(".* (Bruttodividende|Bruttoaussch.ttung|Bruttothesaurierung)") - .match("^.* pro St.ck([:\\s]+)? \\-[\\.,\\d]+ (?[\\w]{3})$") - .assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))) - , - // @formatter:off - // Nr.111111111 ISH.FOOBAR 12345666 x.EFT (DE1234567890/AB1234) - // Zinstermin : 28.04.2016 Zinsbetrag : 73,75 EUR - // @formatter:on - section -> section - .attributes("name", "isin", "wkn", "currency") - .match("^Nr\\.[\\d]+([\\s]+)? (?.*)([\\s]+)? \\((?[A-Z]{2}[A-Z0-9]{9}[0-9])\\/(?[A-Z0-9]{6})\\).*$") - .match("^.* Zinsbetrag([:\\s]+)? [\\.,\\d]+ (?[\\w]{3})$") - .assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))) - ) - - // @formatter:off - // St. : 360 - // @formatter:on - .section("shares") - .match("^(St\\.|St\\.\\/Nominale)([:\\s]+)? (?[\\.,\\d]+).*$") - .assign((t, v) -> t.setShares(asShares(v.get("shares")))) - - // @formatter:off - // Valuta : 17.06.2022 grundlage : 0,00 EUR - // @formatter:on - .section("date") - .match("^Valuta([:\\s]+)? (?[\\d]{2}\\.[\\d]{2}\\.[\\d]{4}).*$") - .assign((t, v) -> t.setDateTime(asDate(v.get("date")))) - - .oneOf( - // @formatter:off - // Endbetrag : 795,15 EUR - // @formatter:on - section -> section - .attributes("amount", "currency") - .match("^.* Endbetrag([:\\s]+)? (?[\\.,\\d]+) (?[\\w]{3})$") - .assign((t, v) -> { - t.setCurrencyCode(asCurrencyCode(v.get("currency"))); - t.setAmount(asAmount(v.get("amount"))); - }) - , - // @formatter:off - // Extag : 07.10.2021 Bruttothesaurierung: 78,81 USD - // Zuflusstag : 08.10.2021 Devisenkurs : 1,156200 - // Endbetrag : -15,24 EUR - // @formatter:on - section -> section - .attributes("type", "fxGross", "fxCurrency", "exchangeRate", "currency") - .match("^.* (?(Bruttodividende|Bruttoaussch.ttung|Bruttothesaurierung))([:\\s]+)? (?[\\.,\\d]+) (?[\\w]{3})$") - .match("^(.* )?Devisenkurs([:\\s]+)? (?[\\.,\\d]+).*$") - .match("^.* Endbetrag([:\\s]+)? \\-[\\.,\\d]+ (?[\\w]{3})$") - .assign((t, v) -> { - type.getCurrentContext().putBoolean("negative", true); - - t.setCurrencyCode(asCurrencyCode(v.get("currency"))); - - // If we have a negative amount and no gross reinvestment, - // we first book the dividends received and then the tax charge - if ("Bruttothesaurierung".equals(v.get("type"))) - { - t.setAmount(0L); - } - else - { - BigDecimal exchangeRate = asExchangeRate(v.get("exchangeRate")); - if (t.getCurrencyCode().contentEquals(asCurrencyCode(v.get("fxCurrency")))) - { - exchangeRate = BigDecimal.ONE.divide(exchangeRate, 10, RoundingMode.HALF_DOWN); - } - type.getCurrentContext().put("exchangeRate", exchangeRate.toPlainString()); + Transaction pdfTransaction = new Transaction<>(); - BigDecimal inverseRate = BigDecimal.ONE.divide(exchangeRate, 10, RoundingMode.HALF_DOWN); + Block firstRelevantLine = new Block("^(Dividendengutschrift|Ertragsmitteilung|Zinsgutschrift).*$"); + type.addBlock(firstRelevantLine); + firstRelevantLine.set(pdfTransaction); - Money fxGross = Money.of(asCurrencyCode(v.get("fxCurrency")), asAmount(v.get("fxGross"))); - Money gross = Money.of(asCurrencyCode(v.get("currency")), - BigDecimal.valueOf(fxGross.getAmount()).multiply(inverseRate) - .setScale(0, RoundingMode.HALF_UP).longValue()); + pdfTransaction // - t.setMonetaryAmount(gross); - } - }) - , - // @formatter:off - // Extag : 13.07.2023 Bruttothesaurierung: -32,86 USD - // Zuflusstag : 14.07.2023 Devisenkurs : 1,118200 - // Endbetrag : -0,15 EUR - // @formatter:on - section -> section - .attributes("type", "fxGross", "fxCurrency", "exchangeRate", "currency") - .match("^.* (?(Bruttodividende|Bruttoaussch.ttung|Bruttothesaurierung))([:\\s]+)? \\-(?[\\.,\\d]+) (?[\\w]{3})$") - .match("^(.* )?Devisenkurs([:\\s]+)? (?[\\.,\\d]+).*$") - .match("^.* Endbetrag([:\\s]+)? \\-[\\.,\\d]+ (?[\\w]{3})$") - .assign((t, v) -> { - type.getCurrentContext().putBoolean("negative", true); + .subject(() -> { + AccountTransaction accountTransaction = new AccountTransaction(); + accountTransaction.setType(AccountTransaction.Type.DIVIDENDS); + return accountTransaction; + }) - t.setCurrencyCode(asCurrencyCode(v.get("currency"))); + .oneOf( // + // @formatter:off + // Nr.716759781 HANN.RUECK SE NA O.N. (DE0008402215/840221) + // Extag : 08.05.2014 Bruttodividende + // Zahlungstag : 08.05.2014 pro Stück : 3,0000 EUR + // @formatter:on + section -> section // + .attributes("name", "isin", "wkn", "currency") // + .match("^Nr\\.[\\d]+([\\s]+)? (?.*)([\\s]+)? \\((?[A-Z]{2}[A-Z0-9]{9}[0-9])\\/(?[A-Z0-9]{6})\\).*$") // + .find(".* (Bruttodividende|Bruttoaussch.ttung|Bruttothesaurierung)") // + .match("^.* pro St.ck([:\\s]+)? [\\.,\\d]+ (?[\\w]{3})$") // + .assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))), + // @formatter:off + // Nr.0123456789 X(IE)-MSCI WRLD MOM. 1CDL (IE00BL25JP72/A1103G) + // St. : 248,34 Bruttothesaurierung + // pro Stück : -0,1323 USD + // @formatter:on + section -> section // + .attributes("name", "isin", "wkn", "currency") // + .match("^Nr\\.[\\d]+([\\s]+)? (?.*)([\\s]+)? \\((?[A-Z]{2}[A-Z0-9]{9}[0-9])\\/(?[A-Z0-9]{6})\\).*$") // + .find(".* (Bruttodividende|Bruttoaussch.ttung|Bruttothesaurierung)") // + .match("^.* pro St.ck([:\\s]+)? \\-[\\.,\\d]+ (?[\\w]{3})$") // + .assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))), + // @formatter:off + // Nr.111111111 ISH.FOOBAR 12345666 x.EFT (DE1234567890/AB1234) + // Zinstermin : 28.04.2016 Zinsbetrag : 73,75 EUR + // @formatter:on + section -> section // + .attributes("name", "isin", "wkn", "currency") // + .match("^Nr\\.[\\d]+([\\s]+)? (?.*)([\\s]+)? \\((?[A-Z]{2}[A-Z0-9]{9}[0-9])\\/(?[A-Z0-9]{6})\\).*$") // + .match("^.* Zinsbetrag([:\\s]+)? [\\.,\\d]+ (?[\\w]{3})$") // + .assign((t, v) -> t.setSecurity(getOrCreateSecurity(v)))) - // If we have a negative amount and no gross reinvestment, - // we first book the dividends received and then the tax charge - if ("Bruttothesaurierung".equals(v.get("type"))) - { - t.setAmount(0L); - } - else - { - BigDecimal exchangeRate = asExchangeRate(v.get("exchangeRate")); - if (t.getCurrencyCode().contentEquals(asCurrencyCode(v.get("fxCurrency")))) - { - exchangeRate = BigDecimal.ONE.divide(exchangeRate, 10, RoundingMode.HALF_DOWN); - } - type.getCurrentContext().put("exchangeRate", exchangeRate.toPlainString()); + .oneOf( // + // @formatter:off + // St. : 360 + // @formatter:on + section -> section // + .attributes("shares") // + .match("^St\\.([:\\s]+)? (?[\\.,\\d]+).*$") // + .assign((t, v) -> t.setShares(asShares(v.get("shares")))), + // @formatter:off + // St./Nominale : 10.000,00 USD + // @formatter:on + section -> section // + .attributes("shares") // + .match("^St\\.\\/Nominale([:\\s]+)? (?[\\.,\\d]+).*$") // + .assign((t, v) -> { + BigDecimal shares = asBigDecimal(v.get("shares")); + t.setShares(Values.Share.factorize(shares.doubleValue() / 100)); + })) - BigDecimal inverseRate = BigDecimal.ONE.divide(exchangeRate, 10, RoundingMode.HALF_DOWN); + // @formatter:off + // Valuta : 17.06.2022 grundlage : 0,00 EUR + // @formatter:on + .section("date") // + .match("^Valuta([:\\s]+)? (?[\\d]{2}\\.[\\d]{2}\\.[\\d]{4}).*$") // + .assign((t, v) -> t.setDateTime(asDate(v.get("date")))) - Money fxGross = Money.of(asCurrencyCode(v.get("fxCurrency")), asAmount(v.get("fxGross"))); - Money gross = Money.of(asCurrencyCode(v.get("currency")), - BigDecimal.valueOf(fxGross.getAmount()).multiply(inverseRate) - .setScale(0, RoundingMode.HALF_UP).longValue()); + .oneOf( // + // @formatter:off + // Extag : 07.10.2021 Bruttothesaurierung: 78,81 USD + // Zuflusstag : 08.10.2021 Devisenkurs : 1,156200 + // Endbetrag : -15,24 EUR + // + // Extag : 13.07.2023 Bruttothesaurierung: -32,86 USD + // Zuflusstag : 14.07.2023 Devisenkurs : 1,118200 + // Endbetrag : -0,15 EUR + // @formatter:on + section -> section // + .attributes("type", "fxGross", "termCurrency", "exchangeRate", "baseCurrency") // + .match("^.*(?(Bruttodividende|Bruttoaussch.ttung|Bruttothesaurierung|Zinsbetrag))([:\\s]+)? (\\-)?(?[\\.,\\d]+) (?[\\w]{3})$") // + .match("^.*Devisenkurs([:\\s]+)? (?[\\.,\\d]+).*$") // + .match("^.* Endbetrag([:\\s]+)? \\-[\\.,\\d]+ (?[\\w]{3})$") // + .assign((t, v) -> { + // @formatter:off + // If we have a negative amount and no gross reinvestment, + // we first book the dividends received and then the tax charge. + // @formatter:on + type.getCurrentContext().putBoolean("negative", true); - t.setMonetaryAmount(gross); - } - }) - , - // @formatter:off - // Extag : 20.01.2020 Bruttothesaurierung: 23,19 EUR - // Endbetrag : -8,26 EUR - // @formatter:on - section -> section - .attributes("type", "amount", "currency") - .match("^.* (?(Bruttodividende|Bruttoaussch.ttung|Bruttothesaurierung))([:\\s]+)? (?[\\.,\\d]+) (?[\\w]{3})$") - .match("^.* Endbetrag([:\\s]+)? \\-[\\.,\\d]+ [\\w]{3}$") - .assign((t, v) -> { - type.getCurrentContext().putBoolean("negative", true); + t.setCurrencyCode(asCurrencyCode(v.get("currency"))); - t.setCurrencyCode(asCurrencyCode(v.get("currency"))); + if ("Bruttothesaurierung".equals(v.get("type"))) + { + t.setAmount(0L); + } + else + { + ExtrExchangeRate rate = asExchangeRate(v); + type.getCurrentContext().putType(rate); - // If we have a negative amount and no gross reinvestment, - // we first book the dividends received and then the tax charge - if ("Bruttothesaurierung".equals(v.get("type"))) - t.setAmount(0L); - else - t.setAmount(asAmount(v.get("amount"))); - }) - ) + Money fxGross = Money.of(rate.getTermCurrency(), asAmount(v.get("fxGross"))); + Money gross = rate.convert(rate.getBaseCurrency(), fxGross); - .optionalOneOf( - // @formatter:off - // Extag : 20.05.2020 Bruttodividende : 25,50 USD - // *Einbeh. Steuer : 2,37 EUR - // Devisenkurs : 1,134800 - // @formatter:on - section -> section - .attributes("fxGross", "termCurrency", "baseCurrency", "exchangeRate") - .match("^(.* )?(Bruttoaussch.ttung|Bruttodividende|Bruttothesaurierung)([:\\s]+)? (?[\\.,\\d]+) (?[\\w]{3}).*$") - .match("^.* [\\*]+Einbeh\\. Steuer([:\\s]+)? [\\.,\\d]+ (?[\\w]{3})$") - .match("^(.* )?Devisenkurs([:\\s]+)? (?[\\.,\\d]+).*$") - .assign((t, v) -> { - ExtrExchangeRate rate = asExchangeRate(v); - type.getCurrentContext().putType(rate); + t.setMonetaryAmount(gross); + } + }), + // @formatter:off + // Extag : 20.01.2020 Bruttothesaurierung: 23,19 EUR + // Endbetrag : -8,26 EUR + // @formatter:on + section -> section // + .attributes("type", "amount", "currency") // + .match("^.* (?(Bruttodividende|Bruttoaussch.ttung|Bruttothesaurierung|Zinsbetrag))([:\\s]+)? (?[\\.,\\d]+) (?[\\w]{3})$") // + .match("^.* Endbetrag([:\\s]+)? \\-[\\.,\\d]+ [\\w]{3}$") // + .assign((t, v) -> { + // @formatter:off + // If we have a negative amount and no gross reinvestment, + // we first book the dividends received and then the tax charge. + // @formatter:on + type.getCurrentContext().putBoolean("negative", true); - Money fxGross = Money.of(rate.getTermCurrency(), asAmount(v.get("fxGross"))); - Money gross = rate.convert(rate.getBaseCurrency(), fxGross); + t.setCurrencyCode(asCurrencyCode(v.get("currency"))); - checkAndSetGrossUnit(gross, fxGross, t, type.getCurrentContext()); - }) - , - // @formatter:off - // Extag : 13.07.2023 Bruttothesaurierung: -32,86 USD - // Valuta : 14.07.2023 *Einbeh. Steuer : 0,15 EUR - // Zuflusstag : 14.07.2023 Devisenkurs : 1,118200 - // @formatter:on - section -> section - .attributes("fxGross", "termCurrency", "baseCurrency", "exchangeRate") - .match("^(.* )?(Bruttoaussch.ttung|Bruttodividende|Bruttothesaurierung)([:\\s]+)? \\-(?[\\.,\\d]+) (?[\\w]{3}).*$") - .match("^.* [\\*]+Einbeh\\. Steuer([:\\s]+)? [\\.,\\d]+ (?[\\w]{3})$") - .match("^(.* )?Devisenkurs([:\\s]+)? (?[\\.,\\d]+).*$") - .assign((t, v) -> { - ExtrExchangeRate rate = asExchangeRate(v); - type.getCurrentContext().putType(rate); + if ("Bruttothesaurierung".equals(v.get("type"))) + t.setAmount(0L); + else + t.setAmount(asAmount(v.get("amount"))); + }), + // @formatter:off + // Endbetrag : 795,15 EUR + // @formatter:on + section -> section // + .attributes("amount", "currency") // + .match("^.* Endbetrag([:\\s]+)? (?[\\.,\\d]+) (?[\\w]{3})$") // + .assign((t, v) -> { + t.setCurrencyCode(asCurrencyCode(v.get("currency"))); + t.setAmount(asAmount(v.get("amount"))); + })) - Money fxGross = Money.of(rate.getTermCurrency(), asAmount(v.get("fxGross"))); - Money gross = rate.convert(rate.getBaseCurrency(), fxGross); + .optionalOneOf( // + // @formatter:off + // Extag : 20.05.2020 Bruttodividende : 25,50 USD + // *Einbeh. Steuer : 2,37 EUR + // Devisenkurs : 1,134800 + // + // Extag : 13.07.2023 Bruttothesaurierung: -32,86 USD + // Valuta : 14.07.2023 *Einbeh. Steuer : 0,15 EUR + // Zuflusstag : 14.07.2023 Devisenkurs : 1,118200 + // + // Zinstermin : 11.09.2023 Zinsbetrag : 162,50 USD + // Valuta : 11.09.2023 *Einbeh. Steuer : 41,67 EUR + // Devisenkurs : 1,072400 + // @formatter:on + section -> section // + .attributes("fxGross", "termCurrency", "baseCurrency", "exchangeRate") // + .match("^.*(Bruttoaussch.ttung|Bruttodividende|Bruttothesaurierung|Zinsbetrag)([:\\s]+)? (\\-)?(?[\\.,\\d]+) (?[\\w]{3}).*$") // + .match("^.* [\\*]+Einbeh\\. Steuer([:\\s]+)? [\\.,\\d]+ (?[\\w]{3})$") // + .match("^.*Devisenkurs([:\\s]+)? (?[\\.,\\d]+).*$") // + .assign((t, v) -> { + ExtrExchangeRate rate = asExchangeRate(v); + type.getCurrentContext().putType(rate); - checkAndSetGrossUnit(gross, fxGross, t, type.getCurrentContext()); - }) - , - // @formatter:off - // Extag : 08.08.2017 Bruttodividende : 26,25 USD - // Devisenkurs : 1,180800 *Einbeh. Steuer : 1,11 EUR - // @formatter:on - section -> section - .attributes("fxGross", "termCurrency", "exchangeRate", "baseCurrency") - .match("^(.* )?(Bruttoaussch.ttung|Bruttodividende|Bruttothesaurierung)([:\\s]+)? (?[\\.,\\d]+) (?[\\w]{3}).*$") - .match("^(.* )?Devisenkurs([:\\s]+)? (?[\\.,\\d]+) ([\\*\\s]+)?Einbeh\\. Steuer([:\\s]+)? [\\.,\\d]+ (?[\\w]{3})$") - .assign((t, v) -> { - ExtrExchangeRate rate = asExchangeRate(v); - type.getCurrentContext().putType(rate); + Money fxGross = Money.of(rate.getTermCurrency(), asAmount(v.get("fxGross"))); + Money gross = rate.convert(rate.getBaseCurrency(), fxGross); - Money fxGross = Money.of(rate.getTermCurrency(), asAmount(v.get("fxGross"))); - Money gross = rate.convert(rate.getBaseCurrency(), fxGross); + checkAndSetGrossUnit(gross, fxGross, t, type.getCurrentContext()); + }), + // @formatter:off + // Extag : 08.08.2017 Bruttodividende : 26,25 USD + // Devisenkurs : 1,180800 *Einbeh. Steuer : 1,11 EUR + // @formatter:on + section -> section // + .attributes("fxGross", "termCurrency", "exchangeRate", "baseCurrency") // + .match("^.*(Bruttoaussch.ttung|Bruttodividende|Bruttothesaurierung|Zinsbetrag)([:\\s]+)? (?[\\.,\\d]+) (?[\\w]{3}).*$") // + .match("^.*Devisenkurs([:\\s]+)? (?[\\.,\\d]+) ([\\*\\s]+)?Einbeh\\. Steuer([:\\s]+)? [\\.,\\d]+ (?[\\w]{3})$") // + .assign((t, v) -> { + ExtrExchangeRate rate = asExchangeRate(v); + type.getCurrentContext().putType(rate); - checkAndSetGrossUnit(gross, fxGross, t, type.getCurrentContext()); - }) - ) + Money fxGross = Money.of(rate.getTermCurrency(), asAmount(v.get("fxGross"))); + Money gross = rate.convert(rate.getBaseCurrency(), fxGross); - .optionalOneOf( - // @formatter:off - // unter der Transaktion-Nr.: 132465978 - // unter der Transaktion-Nr. : 1111111111 - // Transaktionsnummer: 921414163 - // @formatter:on - section -> section - .attributes("note") - .match("^.*(?(Transaktion\\-Nr\\.|Transaktionsnummer)([:\\s]+)? [\\d]+).*$") - .assign((t, v) -> t.setNote(trim(v.get("note")))) - , - // @formatter:off - // Evtl. Details dazu finden Sie im Steuerreport unter der Transaktion-Nr.: - // 1301138113. - // @formatter:on - section -> section - .attributes("note1", "note2") - .match("^.*(?(Transaktion\\-Nr\\.|Transaktionsnummer)([:\\s]+)?)$") - .match("^([\\s]+)?(?[\\d]+)\\.$") - .assign((t, v) -> t.setNote(trim(v.get("note1")) + " " + trim(v.get("note2")))) - ) + checkAndSetGrossUnit(gross, fxGross, t, type.getCurrentContext()); + })) - .wrap(t -> { - // The final amount is negative. The taxes incurred - // are processed in a separate transaction. - // Finally, we remove the flag. - type.getCurrentContext().remove("negative"); + .optionalOneOf( // + // @formatter:off + // unter der Transaktion-Nr.: 132465978 + // unter der Transaktion-Nr. : 1111111111 + // Transaktionsnummer: 921414163 + // @formatter:on + section -> section // + .attributes("note") // + .match("^.*(?(Transaktion\\-Nr\\.|Transaktionsnummer)([:\\s]+)? [\\d]+).*$") // + .assign((t, v) -> t.setNote(trim(v.get("note")))), + // @formatter:off + // Evtl. Details dazu finden Sie im Steuerreport unter der Transaktion-Nr.: + // 1301138113. + // @formatter:on + section -> section // + .attributes("note1", "note2") // + .match("^.*(?(Transaktion\\-Nr\\.|Transaktionsnummer)([:\\s]+)?)$") // + .match("^([\\s]+)?(?[\\d]+)\\.$") // + .assign((t, v) -> t.setNote(trim(v.get("note1")) + " " + trim(v.get("note2"))))) - if (t.getCurrencyCode() != null && t.getAmount() != 0) - return new TransactionItem(t); - return null; - }); + .wrap(t -> { + // The final amount is negative. The taxes incurred + // are processed in a separate transaction. + // Finally, we remove the flag. + type.getCurrentContext().remove("negative"); + + if (t.getCurrencyCode() != null && t.getAmount() != 0) + return new TransactionItem(t); + return null; + }); addTaxesSectionsTransaction(pdfTransaction, type); addFeesSectionsTransaction(pdfTransaction, type); - - block.set(pdfTransaction); } private void addDividendeWithNegativeAmountTransaction()