Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modify Merkur Privatbank PDF-Importer to support new transaction #3938

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,34 @@
PDFBox Version: 1.8.17
Portfolio Performance Version: 0.68.3
-----------------------------------------
Am Marktplatz 10 · 97762 Hammelburg
Depotnummer 123456789

Kundennummer 123456789
Max Mustermann
Abrechnungsnr. 60338188850
Max Mustermann Datum 12.04.2024
Irgendeinestr. 1 Ihr Berater Herr Willi Wichtig
64295 Darmstadt Telefon +49 30 23125001

Ertragsgutschrift nach § 27 KStG
Nominale Wertpapierbezeichnung ISIN (WKN)
Stück 55 DEUTSCHE TELEKOM AG DE0005557508 (555750)
NAMENS-AKTIEN O.N.
Zahlbarkeitstag 15.04.2024 Ertrag pro Stück 0,77 EUR
Bestandsstichtag 10.04.2024
Ex-Tag 11.04.2024
Geschäftsjahr 01.01.2023 - 31.12.2023
Dividendengutschrift nach § 27 KStG 42,35+ EUR
Ausmachender Betrag 42,35+ EUR
Lagerstelle CBF w/7268 w/DZ Bank (600502 / 72680000)
Den Betrag buchen wir mit Wertstellung 15.04.2024 zu Gunsten des Kontos 9001272 (IBAN DE33 6605 0101 0009 0012
72), BLZ 660 501 01 (BIC GENODEF1M06).
Keine Steuerbescheinigung.
Es handelt sich bei der Ausschüttung um Leistungen aus dem steuerlichen Einlagekonto der Kapitalgesellschaft (§27 Abs.
1-7 KStG). Dieser Ertrag unterliegt laut Gesetz zum Zeitpunkt des Zuflusses keinem Steuerabzug und ist
einkommensteuerfrei. Er mindert jedoch im Nachhinein den Kaufkurs der bezogenen Aktie, so dass bei deren Verkauf
möglicherweise ein entsprechend höherer Kursgewinn zu versteuern ist.
Dieses Dokument wurde maschinell erstellt und wird nicht unterschrieben.
Bitte ggf. Rückseite beachten.
6175.04130111.0002202ER01
@@ -1,17 +1,28 @@
package name.abuchen.portfolio.datatransfer.pdf.merkurprivatbank;

import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.deposit;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.dividend;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasAmount;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasCurrencyCode;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasDate;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasFees;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasGrossValue;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasIsin;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasName;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasNote;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasShares;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasSource;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasTaxes;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasTicker;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasWkn;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.removal;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.security;
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.Matchers.hasItem;
import static org.hamcrest.collection.IsEmptyCollection.empty;
import static org.junit.Assert.assertNull;

Expand Down Expand Up @@ -215,6 +226,37 @@ public void testWertpapierVerkauf02()

}

@Test
public void testDividende01()
{
MerkurPrivatBankPDFExtractor extractor = new MerkurPrivatBankPDFExtractor(new Client());

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

List<Item> results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Dividende01.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("DE0005557508"), hasWkn("555750"), hasTicker(null), //
hasName("DEUTSCHE TELEKOM AG NAMENS-AKTIEN O.N."), //
hasCurrencyCode("EUR"))));

// check dividends transaction
assertThat(results, hasItem(dividend( //
hasDate("2024-04-15T00:00"), hasShares(55), //
hasSource("Dividende01.txt"), //
hasNote("Abrechnungsnr. 60338188850"), //
hasAmount("EUR", 42.35), hasGrossValue("EUR", 42.35), //
hasTaxes("EUR", 0.00), hasFees("EUR", 0.00))));
}

@Test
public void testKontoauszug01()
{
Expand Down
Expand Up @@ -24,9 +24,10 @@ public MerkurPrivatBankPDFExtractor(Client client)
super(client);

addBankIdentifier("MERKUR PRIVATBANK KGaA");
addBankIdentifier("Umsatzsteuer-ID DE198159260");
addBankIdentifier("Am Marktplatz 10");

addBuySellTransaction();
addDividendeTransaction();
addAccountStatementTransaction();
}

Expand Down Expand Up @@ -69,7 +70,7 @@ private void addBuySellTransaction()
// Stück 600 XTR.(IE) - MSCI WORLD IE00BJ0KDQ92 (A1XB5U)
// REGISTERED SHARES 1C O.N.
// @formatter:on
.section("name", "name1", "isin", "wkn", "currency") //
.section("name", "isin", "wkn", "name1", "currency") //
.match("^St.ck [\\.,\\d]+ (?<name>.*) (?<isin>[A-Z]{2}[A-Z0-9]{9}[0-9]) \\((?<wkn>[A-Z0-9]{6})\\)$") //
.match("^(?<name1>.*)$") //
.match("^Ausf.hrungskurs [\\.,\\d]+ (?<currency>[\\w]{3}).*$") //
Expand Down Expand Up @@ -118,6 +119,73 @@ private void addBuySellTransaction()

}

private void addDividendeTransaction()
{
DocumentType type = new DocumentType("Ertragsgutschrift nach");
this.addDocumentTyp(type);

Transaction<AccountTransaction> pdfTransaction = new Transaction<>();

Block firstRelevantLine = new Block("^Am Marktplatz.*$");
type.addBlock(firstRelevantLine);
firstRelevantLine.set(pdfTransaction);

pdfTransaction //

.subject(() -> {
AccountTransaction accountTransaction = new AccountTransaction();
accountTransaction.setType(AccountTransaction.Type.DIVIDENDS);
return accountTransaction;
})

// @formatter:off
// Stück 55 DEUTSCHE TELEKOM AG DE0005557508 (555750)
// NAMENS-AKTIEN O.N.
// Zahlbarkeitstag 15.04.2024 Ertrag pro Stück 0,77 EUR
// @formatter:on
.section("name", "isin", "wkn", "nameContinued", "currency") //
.match("^St.ck [\\.,\\d]+ (?<name>.*) (?<isin>[A-Z]{2}[A-Z0-9]{9}[0-9]) \\((?<wkn>[A-Z0-9]{6})\\)$") //
.match("^(?<nameContinued>.*)$") //
.match("^.* Ertrag pro St.ck [\\.,\\d]+ (?<currency>[\\w]{3}).*$") //
.assign((t, v) -> t.setSecurity(getOrCreateSecurity(v)))

// @formatter:off
// Stück 55 DEUTSCHE TELEKOM AG DE0005557508 (555750)
// @formatter:on
.section("shares") //
.match("^St.ck (?<shares>[\\.,\\d]+).*$") //
.assign((t, v) -> t.setShares(asShares(v.get("shares"))))

// @formatter:off
// Zahlbarkeitstag 15.04.2024 Ertrag pro Stück 0,77 EUR
// @formatter:on
.section("date") //
.match("^Zahlbarkeitstag (?<date>[\\d]{2}\\.[\\d]{2}\\.[\\d]{4}).*$") //
.assign((t, v) -> t.setDateTime(asDate(v.get("date"))))

// @formatter:off
// Ausmachender Betrag 42,35+ EUR
// @formatter:on
.section("amount", "currency") //
.match("^Ausmachender Betrag (?<amount>[\\.,\\d]+)\\+ (?<currency>[\\w]{3})$") //
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(asAmount(v.get("amount")));
})

// @formatter:off
// Abrechnungsnr. 60338188850
// @formatter:on
.section("note").optional() //
.match("^.*(?<note>Abrechnungsnr\\. .*)$") //
.assign((t, v) -> t.setNote(trim(v.get("note"))))

.wrap(TransactionItem::new);

addTaxesSectionsTransaction(pdfTransaction, type);
addFeesSectionsTransaction(pdfTransaction, type);
}

private void addAccountStatementTransaction()
{
final DocumentType type = new DocumentType(".*\\-Konto Kontonummer", //
Expand All @@ -142,6 +210,25 @@ private void addAccountStatementTransaction()
type.addBlock(depositRemovalBlock);
}

private <T extends Transaction<?>> void addTaxesSectionsTransaction(T transaction, DocumentType type)
{
transaction //

// @formatter:off
// Kapitalertragsteuer 25,00% auf 12.960,18 EUR 3.240,05- EUR
// @formatter:on
.section("tax", "currency").optional() //
.match("^Kapitalertragsteuer [\\.,\\d]+([\\s]+)?% .* (?<tax>[\\.,\\d]+)\\- (?<currency>[\\w]{3})$") //
.assign((t, v) -> processTaxEntries(t, v, type))

// @formatter:off
// Solidaritätszuschlag 5,50% auf 3.240,05 EUR 178,20- EUR
// @formatter:on
.section("tax", "currency").optional() //
.match("^Solidarit.tszuschlag [\\.,\\d]+([\\s]+)?% .* (?<tax>[\\.,\\d]+)\\- (?<currency>[\\w]{3})$") //
.assign((t, v) -> processTaxEntries(t, v, type));
}

private <T extends Transaction<?>> void addFeesSectionsTransaction(T transaction, DocumentType type)
{
transaction //
Expand All @@ -161,25 +248,6 @@ private <T extends Transaction<?>> void addFeesSectionsTransaction(T transaction
.assign((t, v) -> processFeeEntries(t, v, type));
}

private <T extends Transaction<?>> void addTaxesSectionsTransaction(T transaction, DocumentType type)
{
transaction

// Steuerberechnung
// Kapitalertragsteuer 25,00% auf 12.960,18 EUR
// 3.240,05- EUR
.section("tax", "currency").optional()
.match("^Kapitalertragsteuer [\\.,\\d]+([\\s]+)?% .* (?<tax>[\\.,\\d]+)\\- (?<currency>[\\w]{3})$")
.assign((t, v) -> processTaxEntries(t, v, type))

// Solidaritätszuschlag 5,50% auf 3.240,05 EUR 178,20-
// EUR
// Ausmachender Betrag 116.003,09 EUR
.section("tax", "currency").optional()
.match("^Solidarit.tszuschlag [\\.,\\d]+([\\s]+)?% .* (?<tax>[\\.,\\d]+)\\- (?<currency>[\\w]{3})$")
.assign((t, v) -> processTaxEntries(t, v, type));
}

private Transaction<AccountTransaction> depositRemovalTransaction(DocumentType type, String regex)
{
return new Transaction<AccountTransaction>() //
Expand Down