Skip to content

Commit

Permalink
DKB Erweiterung Quellensteuern Fremdwährung 2019
Browse files Browse the repository at this point in the history
Signed-off-by: ZfT2 <[email protected]>
[removed target platform and launch configuration from commit]
Signed-off-by: Andreas Buchen <[email protected]>
  • Loading branch information
ZfT2 authored and buchen committed Jul 10, 2019
1 parent e75b1a6 commit 2485e1a
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
PDF Author: ''
PDFBox Version: 1.8.16
-----------------------------------------
10919 Berlin Seite 1
Depotnummer 111111111
Kundennummer 111111111
xxxx xxxx
Abrechnungsnr. 111111111
Herrn Datum 27.06.2019
xxxx xxxx
xxxx
11111 xxxx
Dividendengutschrift
Nominale Wertpapierbezeichnung ISIN (WKN)
Stück 715 SEIKO EPSON CORP. JP3414750004 (471496)
REGISTERED SHARES O.N.
Zahlbarkeitstag 27.06.2019 Dividende pro Stück 31,00 JPY
Bestandsstichtag 26.03.2019 Herkunftsland Japan
Ex-Tag 27.03.2019 Art der Dividende Schlussdividende
Geschäftsjahr 01.04.2018 - 31.03.2019
Devisenkurs EUR / JPY 123,0200
Devisenkursdatum 27.06.2019
Dividendengutschrift 22.165,00 JPY 180,17+ EUR
Umrechnung in EUR 180,17 EUR
Einbehaltene Quellensteuer 15,315 % auf 22.165,00 JPY 27,59- EUR
Anrechenbare Quellensteuer 15 % auf 180,17 EUR 27,03 EUR
Kapitalertragsteuerpflichtige Dividende 180,17 EUR
Verrechnete anrechenbare ausländische Quellensteuer
(Verhältnis 100/25) auf 27,03 EUR 108,12 - EUR
Berechnungsgrundlage für die Kapitalertragsteuer 72,05 EUR
Kapitalertragsteuer 25 % auf 72,05 EUR 18,01- EUR
Solidaritätszuschlag 5,5 % auf 18,01 EUR 0,99- EUR
Ausmachender Betrag 133,58+ EUR
0,315 % rückforderbare Quellensteuer 69,82 JPY
Lagerstelle Clearstream Banking Lux (849133 / 64003)
Den Betrag buchen wir mit Wertstellung 01.07.2019 zu Gunsten des Kontos 11111 (IBAN DExxxx), BLZ 120 300 00 (BIC BYLADEM1001).
Bitte ggf. Rückseite beachten.
11111.11111.11111ER01

Seite 2
Depotnummer 111111111
Kundennummer 111111111
Abrechnungsnr. 111111111
Datum 27.06.2019
Keine Steuerbescheinigung.
Nachrichtlich die Übersicht Ihrer Verrechnungs- und Steuertopfsalden zum Zeitpunkt der Erstellung der Abrechnung.
Verrechnungstöpfe 2019 Berechnungsgrundlage
der gezahlten Steuern
Euro Aktien Sonstige Sparer- anrechenbare Aktien und Sonstige
Pauschbetrag Quellensteuer
Vorher 0,00 0,00 0,00 0,00 0,00
Ertrag 0,00
0,00 0,00 0,00 0,00 0,00
Nachher 0,00 0,00 0,00 0,00 0,00
Dieses Dokument wurde maschinell erstellt und wird nicht unterschrieben.
11111.11111.11111ER01
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,18 @@ private Security assertSecurityErtragsgutschriftDividendeFremdwaehrung(List<Item

return security;
}

private Security assertSecurityErtragsgutschriftDividendeFremdwaehrung2(List<Item> results)
{
Optional<Item> item = results.stream().filter(i -> i instanceof SecurityItem).findFirst();
assertThat(item.isPresent(), is(true));
Security security = ((SecurityItem) item.get()).getSecurity();
assertThat(security.getIsin(), is("JP3414750004"));
assertThat(security.getWkn(), is("471496"));
assertThat(security.getName(), is("SEIKO EPSON CORP. REGISTERED SHARES O.N."));

return security;
}

private Security assertSecurityErtragsgutschriftDividendeQuellensteuern(List<Item> results)
{
Expand Down Expand Up @@ -359,6 +371,38 @@ public void testErtragsgutschriftDividendeFremdwaehrung() throws IOException
assertThat(transaction.getAmount(), is(779L));
assertThat(transaction.getShares(), is(Values.Share.factorize(53)));
}

@Test
public void testErtragsgutschriftDividendeFremdwaehrung2() throws IOException
{
DkbPDFExtractor extractor = new DkbPDFExtractor(new Client());

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

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

assertThat(errors, empty());
assertThat(results.size(), is(2));

// check security
Security security = assertSecurityErtragsgutschriftDividendeFremdwaehrung2(results);

// check transaction
Optional<Item> item = results.stream().filter(i -> i instanceof TransactionItem).findFirst();
assertThat(item.isPresent(), is(true));
assertThat(item.get().getSubject(), instanceOf(AccountTransaction.class));
AccountTransaction transaction = (AccountTransaction) item.get().getSubject();
assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS));
assertThat(transaction.getSecurity(), is(security));
assertThat(transaction.getCurrencyCode(), is(CurrencyUnit.EUR));
assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2019-07-01T00:00")));
assertThat(transaction.getAmount(), is(13358L));
assertThat(transaction.getShares(), is(Values.Share.factorize(715)));

assertThat(transaction.getUnitSum(Unit.Type.TAX),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(46.59))));
}

@Test
public void testErtragsgutschriftDividendeQuellensteuern() throws IOException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
public class DkbPDFExtractor extends AbstractPDFExtractor
{
private static final String EXCHANGE_RATE = "exchangeRate"; //$NON-NLS-1$
private static final String FLAG_WITHHOLDING_TAX_FOUND = "exchangeRate"; //$NON-NLS-1$

public DkbPDFExtractor(Client client)
{
Expand Down Expand Up @@ -228,7 +229,8 @@ private void addInterestTransaction()

private void addDividendTransaction()
{
DocumentType type = new DocumentType("Dividendengutschrift");
DocumentType type = createDocumentType("Dividendengutschrift");
//DocumentType type = new DocumentType("Dividendengutschrift");
this.addDocumentTyp(type);

Block block = new Block("Dividendengutschrift|Ertragsgutschrift.*");
Expand Down Expand Up @@ -386,16 +388,7 @@ private void addInvestmentPayoutTransaction()
private void addTransaction(String documentTypeString, String blockMarkerString,
AccountTransaction.Type transactiontype)
{
DocumentType type = new DocumentType(documentTypeString, (context, lines) -> {
Pattern pattern = Pattern
.compile("Devisenkurs (?<term>\\w{3}+) / (?<base>\\w{3}+) (?<exchangeRate>[\\d,.]*)(.*)");
for (String line : lines)
{
Matcher m = pattern.matcher(line);
if (m.matches())
context.put(EXCHANGE_RATE, m.group("exchangeRate"));
}
});
DocumentType type = createDocumentType(documentTypeString);
this.addDocumentTyp(type);

Block block = new Block(blockMarkerString);
Expand All @@ -409,7 +402,8 @@ private void addTransaction(String documentTypeString, String blockMarkerString,
});
block.set(pdfTransaction);

pdfTransaction.oneOf(section -> section.attributes("shares", "name", "isin", "wkn", "currency")
pdfTransaction.oneOf(
section -> section.attributes("shares", "name", "isin", "wkn", "currency")
.find("Nominale Wertpapierbezeichnung ISIN \\(WKN\\)")
.match("(^St\\Dck) (?<shares>[\\d,.]*) (?<name>.*)$") //
.match(".*") //
Expand Down Expand Up @@ -444,6 +438,21 @@ private void addTransaction(String documentTypeString, String blockMarkerString,
addTaxesSectionsTransaction(type, pdfTransaction);
addFeesSectionsTransaction(pdfTransaction);
}

private DocumentType createDocumentType(String documentTypeString)
{
DocumentType type = new DocumentType(documentTypeString, (context, lines) -> {
Pattern pattern = Pattern
.compile("Devisenkurs (?<term>\\w{3}+) / (?<base>\\w{3}+) (?<exchangeRate>[\\d,.]*)(.*)");
for (String line : lines)
{
Matcher m = pattern.matcher(line);
if (m.matches())
context.put(EXCHANGE_RATE, m.group("exchangeRate"));
}
});
return type;
}

private void addBuyTransactionFundsSavingsPlan()
{
Expand Down Expand Up @@ -509,34 +518,60 @@ private <T extends Transaction<?>> void addTaxesSectionsTransaction(DocumentType
.match("^Kirchensteuer (.*) (\\w{3}+) (?<kirchenst>[\\d.-]+,\\d+)(-) (?<currency>\\w{3}+)")
.assign((t, v) -> addTax(documentType, t, v, "kirchenst"))

.section("quellenst", "currency").optional()
.match("^Anrechenbare Quellensteuer(.*) (\\w{3}+) (?<quellenst>[\\d.]+,\\d+) (?<currency>\\w{3}+)")
.assign((t, v) -> addTax(documentType, t, v, "quellenst"))
.section("quellensteinbeh", "currency").optional()
.match("^Einbehaltene Quellensteuer(.*) (\\w{3}+) (?<quellensteinbeh>[\\d.]+,\\d+)(-) (?<currency>\\w{3}+)")
.assign((t, v) -> {
documentType.getCurrentContext().put(FLAG_WITHHOLDING_TAX_FOUND, "true");
addTax(documentType, t, v, "quellensteinbeh");
})

.section("quellenstanr", "currency").optional()
.match("^Anrechenbare Quellensteuer(.*) (\\w{3}+) (?<quellenstanr>[\\d.]+,\\d+) (?<currency>\\w{3}+)")
.assign((t, v) -> addTax(documentType, t, v, "quellenstanr"))

.section("quellenstrueck", "currency").optional()
.match("^(.*)ckforderbare Quellensteuer (?<quellenstrueck>[\\d.]+,\\d+) (?<currency>\\w{3}+)")
.assign((t, v) -> addTax(documentType, t, v, "quellenstrueck"));

}

private void addTax(DocumentType documentType, Object t, Map<String, String> v, String taxtype)
{
name.abuchen.portfolio.model.Transaction tx = getTransaction(t);
// Wenn es 'Einbehaltene Quellensteuer' gibt, dann die weiteren
// Quellensteuer-Arten nicht berücksichtigen.
// Die Berechnung der Gesamt-Quellensteuer anhand der anrechenbaren- und
// der rückforderbaren Steuer kann ansonsten zu Rundungsfehlern führen.
if (checkWithholdingTax(documentType, taxtype))
{

String currency = asCurrencyCode(v.get("currency"));
long amount = asAmount(v.get(taxtype));
name.abuchen.portfolio.model.Transaction tx = getTransaction(t);

if (!currency.equals(tx.getCurrencyCode()) && documentType.getCurrentContext().containsKey(EXCHANGE_RATE))
{
BigDecimal rate = BigDecimal.ONE.divide( //
asExchangeRate(documentType.getCurrentContext().get(EXCHANGE_RATE)), 10,
RoundingMode.HALF_DOWN);
String currency = asCurrencyCode(v.get("currency"));
long amount = asAmount(v.get(taxtype));

currency = tx.getCurrencyCode();
amount = rate.multiply(BigDecimal.valueOf(amount)).setScale(0, RoundingMode.HALF_UP).longValue();
}
if (!currency.equals(tx.getCurrencyCode()) && documentType.getCurrentContext().containsKey(EXCHANGE_RATE))
{
BigDecimal rate = BigDecimal.ONE.divide( //
asExchangeRate(documentType.getCurrentContext().get(EXCHANGE_RATE)), 10,
RoundingMode.HALF_DOWN);

tx.addUnit(new Unit(Unit.Type.TAX, Money.of(currency, amount)));
currency = tx.getCurrencyCode();
amount = rate.multiply(BigDecimal.valueOf(amount)).setScale(0, RoundingMode.HALF_DOWN).longValue();
}

tx.addUnit(new Unit(Unit.Type.TAX, Money.of(currency, amount)));
}
}

private boolean checkWithholdingTax(DocumentType documentType, String taxtype)
{
if (Boolean.valueOf(documentType.getCurrentContext().get(FLAG_WITHHOLDING_TAX_FOUND)))
{
if ("quellenstanr".equalsIgnoreCase(taxtype) || ("quellenstrueck".equalsIgnoreCase(taxtype)))
{
return false;
}
}
return true;
}

private <T extends Transaction<?>> void addFeesSectionsTransaction(T pdfTransaction)
Expand Down

0 comments on commit 2485e1a

Please sign in to comment.