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 Postbank PDF-Importer to support new transaction #2977

Merged
merged 1 commit into from Oct 3, 2022
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,55 @@
PDFBox Version: 1.8.16
Portfolio Performance Version: 0.59.1
-----------------------------------------
Postbank Köln · 51222 Köln Seite 1
Depotnummer
876543210
Kundennummer 123456789
Max Mustermann
Abrechnungsnr. 11223344550
Herr Datum 12.09.2022
Max Musterman
Musterstr. 1
11222 Musterstadt



Ausschüttung Investmentfonds
Nominale Wertpapierbezeichnung ISIN (WKN)
Stück 1.700 CIVITAS SOCIAL HOUSING PLC GB00BD8HBD32 (A2AN2J)
REGISTERED SHARES LS -,01
Zahlbarkeitstag 09.09.2022 Ausschüttung pro St. 0,014250000 GBP
Bestandsstichtag 17.08.2022 Herkunftsland Großbritannien und Nordirland
Ex-Tag 18.08.2022
Geschäftsjahr 01.04.2022 - 31.03.2023
Devisenkurs EUR / GBP 0,8728
Devisenkursdatum 12.09.2022
Ausschüttung 19,38 GBP 22,20+ EUR
Einbehaltene Quellensteuer 20 % auf 19,38 GBP 4,44- EUR
Anrechenbare Quellensteuer pro Stück 0,0021375 GBP 3,63 GBP
Kapitalertragsteuerpflichtiger Ertrag 24,23 GBP
Umrechnung in EUR 27,76 EUR
Verrechneter Sparer-Pauschbetrag 27,76 - EUR
Berechnungsgrundlage für die Kapitalertragsteuer 0,00 EUR
Ausmachender Betrag 17,76+ EUR
5 % rückforderbare Quellensteuer 0,97 GBP
Lagerstelle CBL w/Sonderheiten (860973 / 60045)
Den Betrag buchen wir mit Wertstellung 13.09.2022 zu Gunsten des Kontos 222444488 (IBAN DE43 3701 1000 0222 4444
88), BLZ 370 110 00 (BIC PBNKDEFFXXX).
Keine Steuerbescheinigung.
Bitte ggf. Rückseite beachten.
0880.09999000.00066664400

Seite 2
Depotnummer 876543210
Kundennummer 123456789
Abrechnungsnr. 11223344550
Datum 12.09.2022
Nachrichtlich die Übersicht Ihrer Verrechnungs- und Steuertopfsalden zum Zeitpunkt der Erstellung der Abrechnung.
Verrechnungstöpfe 2022 Berechnungsgrundlage
der gezahlten Steuern
Euro Aktien Sonstige Sparer- anrechenbare Aktien Sonstige
Pauschbetrag Quellensteuer
Nachher 0,00 0,00 111,11 22,22 0,00 0,00
Dieses Dokument wurde maschinell erstellt und wird nicht unterschrieben.
0860.09130100.0002500ER03
@@ -0,0 +1,55 @@
PDFBox Version: 1.8.16
Portfolio Performance Version: 0.59.1
-----------------------------------------
Postbank Köln · 51222 Köln Seite 1 von 2
Depotnummer
876543210
Kundennummer 123456789
Max Mustermann
Auftragsnummer 69123/22.01
Herr Datum 01.08.2022
Max Mustermann Rechnungsnummer W00860-0001110044/11
Stadtgebiet Umsatzsteuer-ID DE222103339
Max Musterman
Musterstr. 1
11222 Musterstadt

Wertpapier Abrechnung Kauf
Mischkursabrechnung
Auftrag vom 29.07.2022 17:34:38 Uhr
Nominale Wertpapierbezeichnung ISIN (WKN)
Stück 1.700 CIVITAS SOCIAL HOUSING PLC GB00BD8HBD32 (A2AN2J)
REGISTERED SHARES LS -,01
Handels-/Ausführungsplatz London (gemäß Weisung)
Börsensegment XLON
Limit-Order
Limit 0,84 GBP
Schlusstag/-Zeit 29.07.2022 17:35:23
Ortszeit 29.07.2022 16:35:23
Ausführungskurs 0,828 GBP
Wertpapierrechnung Lagerland Grossbritannien
Devisenkurs (EUR/GBP) 0,8328 vom 01.08.2022
Kurswert 1.690,20- EUR
Provision 42,95- EUR
Fremde Auslagen 16,86- EUR
Übertragungs-/Liefergebühr 15,15- EUR
Ausmachender Betrag 1.765,16- EUR

Den Gegenwert buchen wir mit Valuta 02.08.2022 zu Lasten des Kontos 222444488
(IBAN DE43 3701 1000 0222 4444 88), BLZ 37011000 (BIC PBNKDEFFXXX).
Die Wertpapiere schreiben wir Ihrem Depotkonto gut.
Sofern keine Umsatzsteuer ausgewiesen ist, handelt es sich um eine umsatzsteuerbefreite Finanzdienstleistung.

Der Abrechnungsmischkurs/-preis ergibt sich wie folgt:
0860.08022777.0001111OR07
Seite 2 von 2
Depotnummer 876543210
Kundennummer 123456789
Auftragsnummer 69123/22.01
Datum 01.08.2022
Stück Kurs Zeit Stück Kurs Zeit
1.547 0,828 17:35:23 153 0,828 17:35:23
Für das Geschäft wurde keine Anlageberatung erbracht.
Auf Risiken gemäß WpHG wurde hingewiesen.
Dieses Dokument wurde maschinell erstellt und wird nicht unterschrieben.
0860.08011908.0005292OR07
Expand Up @@ -191,6 +191,101 @@ public void testWertpapierKauf03()
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(0.90))));
}

@Test
public void testWertpapierKauf04()
{
PostbankPDFExtractor extractor = new PostbankPDFExtractor(new Client());

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

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

assertThat(errors, empty());
assertThat(results.size(), is(2));
new AssertImportActions().check(results, CurrencyUnit.EUR);

// check security
Security security = results.stream().filter(SecurityItem.class::isInstance).findFirst()
.orElseThrow(IllegalArgumentException::new).getSecurity();
assertThat(security.getIsin(), is("GB00BD8HBD32"));
assertThat(security.getWkn(), is("A2AN2J"));
assertThat(security.getName(), is("CIVITAS SOCIAL HOUSING PLC REGISTERED SHARES LS -,01"));
assertThat(security.getCurrencyCode(), is("GBP"));

// check buy sell transaction
BuySellEntry entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).findFirst()
.orElseThrow(IllegalArgumentException::new).getSubject();

assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY));
assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY));

assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2022-07-29T17:35:23")));
assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1700)));
assertThat(entry.getSource(), is("Kauf04.txt"));
assertThat(entry.getNote(), is("Limit 0,84 GBP"));

assertThat(entry.getPortfolioTransaction().getMonetaryAmount(),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(1765.16))));
assertThat(entry.getPortfolioTransaction().getGrossValue(),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(1690.20))));
assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(0.00))));
assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(42.95 + 16.86 + 15.15))));

Unit grossValueUnit = entry.getPortfolioTransaction().getUnit(Unit.Type.GROSS_VALUE)
.orElseThrow(IllegalArgumentException::new);
assertThat(grossValueUnit.getForex(), is(Money.of("GBP", Values.Amount.factorize(1407.60))));
}

@Test
public void testWertpapierKauf04WithSecurityInEUR()
{
Security security = new Security("CIVITAS SOCIAL HOUSING PLC REGISTERED SHARES LS -,01", CurrencyUnit.EUR);
security.setIsin("GB00BD8HBD32");
security.setWkn("A2AN2J");

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

PostbankPDFExtractor extractor = new PostbankPDFExtractor(client);

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

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

assertThat(errors, empty());
assertThat(results.size(), is(1));
new AssertImportActions().check(results, CurrencyUnit.EUR);

// check buy sell transaction
BuySellEntry entry = (BuySellEntry) results.stream().filter(BuySellEntryItem.class::isInstance).findFirst()
.orElseThrow(IllegalArgumentException::new).getSubject();

assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY));
assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY));

assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2022-07-29T17:35:23")));
assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(1700)));
assertThat(entry.getSource(), is("Kauf04.txt"));
assertThat(entry.getNote(), is("Limit 0,84 GBP"));

assertThat(entry.getPortfolioTransaction().getMonetaryAmount(),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(1765.16))));
assertThat(entry.getPortfolioTransaction().getGrossValue(),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(1690.20))));
assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(0.00))));
assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(42.95 + 16.86 + 15.15))));

CheckCurrenciesAction c = new CheckCurrenciesAction();
Account account = new Account();
account.setCurrencyCode(CurrencyUnit.EUR);
Status s = c.process(entry, account, entry.getPortfolio());
assertThat(s, is(Status.OK_STATUS));
}

@Test
public void testWertpapierVerkauf01()
{
Expand Down Expand Up @@ -918,4 +1013,97 @@ public void testDividende07WithSecurityInEUR()
Status s = c.process(transaction, account);
assertThat(s, is(Status.OK_STATUS));
}

@Test
public void testDividende08()
{
Client client = new Client();

PostbankPDFExtractor extractor = new PostbankPDFExtractor(client);

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

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

assertThat(errors, empty());
assertThat(results.size(), is(2));
new AssertImportActions().check(results, CurrencyUnit.EUR);

// check security
Security security = results.stream().filter(SecurityItem.class::isInstance).findFirst()
.orElseThrow(IllegalArgumentException::new).getSecurity();
assertThat(security.getIsin(), is("GB00BD8HBD32"));
assertThat(security.getWkn(), is("A2AN2J"));
assertThat(security.getName(), is("CIVITAS SOCIAL HOUSING PLC REGISTERED SHARES LS -,01"));
assertThat(security.getCurrencyCode(), is("GBP"));

// check dividends transaction
AccountTransaction transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance)
.findFirst().orElseThrow(IllegalArgumentException::new).getSubject();

assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS));

assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2022-09-09T00:00")));
assertThat(transaction.getShares(), is(Values.Share.factorize(1700)));
assertThat(transaction.getSource(), is("Dividende08.txt"));
assertNull(transaction.getNote());

assertThat(transaction.getMonetaryAmount(),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(17.76))));
assertThat(transaction.getGrossValue(),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(21.92))));
assertThat(transaction.getUnitSum(Unit.Type.TAX),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(3.63 / 0.8728))));
assertThat(transaction.getUnitSum(Unit.Type.FEE),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(0.00))));

Unit grossValueUnit = transaction.getUnit(Unit.Type.GROSS_VALUE).orElseThrow(IllegalArgumentException::new);
assertThat(grossValueUnit.getForex(), is(Money.of("GBP", Values.Amount.factorize(19.13))));
}

@Test
public void testDividende08WithSecurityInEUR()
{
Security security = new Security("CIVITAS SOCIAL HOUSING PLC REGISTERED SHARES LS -,01", CurrencyUnit.EUR);
security.setIsin("GB00BD8HBD32");
security.setWkn("A2AN2J");

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

PostbankPDFExtractor extractor = new PostbankPDFExtractor(client);

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

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

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

// check dividends transaction
AccountTransaction transaction = (AccountTransaction) results.stream().filter(TransactionItem.class::isInstance)
.findFirst().orElseThrow(IllegalArgumentException::new).getSubject();

assertThat(transaction.getType(), is(AccountTransaction.Type.DIVIDENDS));

assertThat(transaction.getDateTime(), is(LocalDateTime.parse("2022-09-09T00:00")));
assertThat(transaction.getShares(), is(Values.Share.factorize(1700)));
assertThat(transaction.getSource(), is("Dividende08.txt"));
assertNull(transaction.getNote());

assertThat(transaction.getMonetaryAmount(),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(17.76))));
assertThat(transaction.getGrossValue(),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(21.92))));
assertThat(transaction.getUnitSum(Unit.Type.TAX),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(3.63 / 0.8728))));
assertThat(transaction.getUnitSum(Unit.Type.FEE),
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(0.00))));

CheckCurrenciesAction c = new CheckCurrenciesAction();
Account account = new Account();
account.setCurrencyCode(CurrencyUnit.EUR);
Status s = c.process(transaction, account);
assertThat(s, is(Status.OK_STATUS));
}
}
Expand Up @@ -118,7 +118,7 @@ private void addBuySellTransaction()
// Schlusstag/-Zeit 04.02.2020 08:00:04
section -> section
.attributes("date", "time")
.match("^Schlusstag\\/-Zeit (?<date>[\\d]{2}\\.[\\d]{2}\\.[\\d]{4}) (?<time>[\\d]{2}:[\\d]{2}:[\\d]{2})( .*)?$")
.match("^Schlusstag\\/-Zeit (?<date>[\\d]{2}\\.[\\d]{2}\\.[\\d]{4}) (?<time>[\\d]{2}:[\\d]{2}:[\\d]{2}).*$")
.assign((t, v) -> t.setDate(asDate(v.get("date"), v.get("time"))))
,
// Den Gegenwert buchen wir mit Valuta 14.01.2020 zu Gunsten des Kontos 012345678
Expand All @@ -142,7 +142,7 @@ private void addBuySellTransaction()
.section( "fxCurrency", "baseCurrency", "termCurrency", "exchangeRate", "gross", "currency").optional()
.match("^(Ausf.hrungskurs|Abrech\\.\\-Preis) [\\.,\\d]+ (?<fxCurrency>[\\w]{3})$")
.match("^Devisenkurs \\((?<baseCurrency>[\\w]{3})\\/(?<termCurrency>[\\w]{3})\\) (?<exchangeRate>[\\.,\\d]+) .*$")
.match("^Kurswert (?<gross>[\\.,\\d]+) (?<currency>[\\w]{3})$")
.match("^Kurswert (?<gross>[\\.,\\d]+)(\\-)? (?<currency>[\\w]{3})$")
.assign((t, v) -> {
PDFExchangeRate rate = asExchangeRate(v);
type.getCurrentContext().putType(rate);
Expand All @@ -158,6 +158,8 @@ private void addBuySellTransaction()
.match("^(?<note>Limit .*)$")
.assign((t, v) -> t.setNote(trim(v.get("note"))))

.conclude(PDFExtractorUtils.fixGrossValueBuySell())

.wrap(BuySellEntryItem::new);

addTaxesSectionsTransaction(pdfTransaction, type);
Expand Down Expand Up @@ -233,6 +235,8 @@ private void addDividendeTransaction()
.match("^.* Art der Dividende (?<note>.*)$")
.assign((t, v) -> t.setNote(trim(v.get("note"))))

.conclude(PDFExtractorUtils.fixGrossValueA())

.wrap(TransactionItem::new);

addTaxesSectionsTransaction(pdfTransaction, type);
Expand Down Expand Up @@ -371,6 +375,11 @@ private <T extends Transaction<?>> void addFeesSectionsTransaction(T transaction
// Übertragungs-/Liefergebühr 0,65- EUR
.section("fee", "currency").optional()
.match("^.bertragungs\\-\\/Liefergeb.hr (?<fee>[\\.,\\d]+)\\- (?<currency>[\\w]{3})$")
.assign((t, v) -> processFeeEntries(t, v, type))

// Fremde Auslagen 16,86- EUR
.section("fee", "currency").optional()
.match("^Fremde Auslagen (?<fee>[\\.,\\d]+)\\- (?<currency>[\\w]{3})$")
.assign((t, v) -> processFeeEntries(t, v, type));
}
}