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 Liechtensteinische Landesbank AG PDF-Importer to support new transaction #3707

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,36 @@
PDFBox Version: 1.8.17
Portfolio Performance Version: 0.67.0
-----------------------------------------
wiLLBe powered by
Liechtensteinische Landesbank
Aktiengesellschaft
Städtle 44 · Postfach 384
9490 Vaduz · Liechtenstein
Sitz Vaduz
Registerbehörde: AJU
HR FL-0001.000.289-1
service@willbe-invest.com
Name Nachname www.willbe-invest.com
Straße X
XXXXX Stadt Vaduz, 18. September 2023
DEUTSCHLAND
Portfolionummer XXXX.XXXX.XXXX
Wahldividende
Auftragsnummer XXXXXXXXX
Reg Shs Pearson PLC
Ihr Bestand per 10.08.2023 17.943232 Stück
ISIN GB0006776081
Valorennummer 400018
Zahlungsdatum 18.09.2023
Ex-Datum 10.08.2023
Bardividende 17.943232 Stück
Zahlungswert GBP 0.07 pro Stück
Bruttobetrag GBP 1.26
Dividende GBP 1.26
Nettobetrag GBP 1.26
Umrechnungskurs GBP/CHF 1.104054
Zu Ihren Gunsten Valuta 18. September 2023 CHF 1.39
Den Betrag haben wir gutgeschrieben: Konto XXXX.XXXX.XXXX wiLLBe Investmentkonto CHF.
Gutschrift - Eingang vorbehalten
Elektronisch zugestellt
SECTRX2 XXXXXXX XXXXXXX XXXXXXX-XXXXXXX-XXXXXXX EBANKING
@@ -0,0 +1,44 @@
PDFBox Version: 1.8.17
Portfolio Performance Version: 0.67.0
-----------------------------------------
wiLLBe powered by
Liechtensteinische Landesbank
Aktiengesellschaft
Städtle 44 · Postfach 384
9490 Vaduz · Liechtenstein
Sitz Vaduz
Registerbehörde: AJU
HR FL-0001.000.289-1
service@willbe-invest.com
Max Mustermann www.willbe-invest.com
Musterstr. 123
01234 Musterstadt Vaduz, 31. Dezember 2023
DEUTSCHLAND
Kontonummer 1234.5678.9012 Tagesgeldkonto wiLLBe EUR
IBAN LI03 0880 0123 4567 8901 2 / BIC LILALI2X
Kontoauszug in EUR 01.12.2023 - 31.12.2023
Datum Buchungstext / Details Valuta Zu Ihren Lasten Zu Ihren Gunsten Saldo in EUR
30.11. Saldovortrag 50'157.97
31.12. Abschluss 31.12. 456.60 50'614.57
Per 31. Dezember 2023
Abrechnungsperiode 30.09.2023-31.12.2023
Zinsusanz Schweizer Usanz (30/360)
Habenzins 456.60
Auftragsnummer: 999999999
Saldo per 31.12.2023 50'614.57
Umsatz 0.00 456.60
Konditionen
Sollzinsen
01.10.2023 - 31.12.2023 0.00 %
Habenzinsen
01.10.2023 - 31.12.2023 3.80 % bis 50'000.00
3.50 % bis 100'000.00
0.25 % über 100'000.00
Bei Überschreitungen des laufenden Kontos beträgt der aktuelle Sollzinssatz 0 % pro Jahr ab dem Zeitpunkt der Überschreitung.
Als Referenzzinssatz gilt: EURIBOR 3M, zuzüglich einer Marge. Der Sollzinssatz verändert sich bei der Änderung des
Referenzzinssatzes. Vorbehalten bleiben anderslautende Vereinbarungen.
Einlagen auf diesem Konto sind nach Massgabe des Einlagensicherungs- und Anlegerentschädigungsgesetzes (EAG)
grundsätzlich erstattungsfähig. Nähere Informationen können dem Informationsbogen für Einleger entnommen werden, den
Sie auch unter www.llb.li/eag einsehen können.
Elektronisch zugestellt
ACC_STMT_MTH_DT_LLB 20231229 99999-999999999-999999999 EBANKING
Expand Up @@ -16,6 +16,7 @@
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.interest;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.purchase;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.sale;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.security;
Expand Down Expand Up @@ -44,6 +45,7 @@
import name.abuchen.portfolio.model.Portfolio;
import name.abuchen.portfolio.model.PortfolioTransaction;
import name.abuchen.portfolio.model.Security;
import name.abuchen.portfolio.money.CurrencyUnit;

@SuppressWarnings("nls")
public class LiechtensteinischeLandesbankAGPDFExtractorTest
Expand Down Expand Up @@ -449,4 +451,96 @@ public void testDividende01WithSecurityInCHF()
assertThat(s, is(Status.OK_STATUS));
}))));
}

@Test
public void testDividende02()
{
LiechtensteinischeLandesbankAGPDFExtractor extractor = new LiechtensteinischeLandesbankAGPDFExtractor(new Client());

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

List<Item> results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Dividende02.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, "CHF");

// check security
assertThat(results, hasItem(security( //
hasIsin("GB0006776081"), hasWkn("400018"), hasTicker(null), //
hasName("Reg Shs Pearson PLC"), //
hasCurrencyCode("GBP"))));

// check dividends transaction
assertThat(results, hasItem(dividend( //
hasDate("2023-09-18T00:00"), hasShares(17.943232), //
hasSource("Dividende02.txt"), //
hasNote("Auftragsnummer XXXXXXXXX"), //
hasAmount("CHF", 1.39), hasGrossValue("CHF", 1.39), //
hasForexGrossValue("GBP", 1.26), //
hasTaxes("CHF", 0.00), hasFees("CHF", 0.00))));
}

@Test
public void testDividende02WithSecurityInCHF()
{
Security security = new Security("Reg Shs Pearson PLC", "CHF");
security.setIsin("GB0006776081");
security.setWkn("400018");

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

LiechtensteinischeLandesbankAGPDFExtractor extractor = new LiechtensteinischeLandesbankAGPDFExtractor(client);

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

List<Item> results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Dividende02.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, "CHF");

// check dividends transaction
assertThat(results, hasItem(dividend( //
hasDate("2023-09-18T00:00"), hasShares(17.943232), //
hasSource("Dividende02.txt"), //
hasNote("Auftragsnummer XXXXXXXXX"), //
hasAmount("CHF", 1.39), hasGrossValue("CHF", 1.39), //
hasTaxes("CHF", 0.00), hasFees("CHF", 0.00), //
check(tx -> {
CheckCurrenciesAction c = new CheckCurrenciesAction();
Account account = new Account();
account.setCurrencyCode("CHF");
Status s = c.process((AccountTransaction) tx, account);
assertThat(s, is(Status.OK_STATUS));
}))));
}

@Test
public void testKontoauzug01()
{
LiechtensteinischeLandesbankAGPDFExtractor extractor = new LiechtensteinischeLandesbankAGPDFExtractor(new Client());

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

List<Item> results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Kontoauszug01.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);

// assert transaction
assertThat(results, hasItem(interest(hasDate("2023-12-31"), hasAmount("EUR", 456.60), //
hasSource("Kontoauszug01.txt"), hasNote("30.09.2023 - 31.12.2023"))));
}
}
Expand Up @@ -36,6 +36,7 @@ public LiechtensteinischeLandesbankAGPDFExtractor(Client client)

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

@Override
Expand Down Expand Up @@ -144,12 +145,14 @@ private void addBuySellTransaction()

private void addDividendeTransaction()
{
DocumentType type = new DocumentType("Bardividende \\(Ordentliche Dividende\\)");
DocumentType type = new DocumentType("(Bardividende \\(Ordentliche Dividende\\)" //
+ "|Wahldividende)");
this.addDocumentTyp(type);

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

Block firstRelevantLine = new Block("^Bardividende \\(Ordentliche Dividende\\)$");
Block firstRelevantLine = new Block("^(Bardividende \\(Ordentliche Dividende\\)" //
+ "|Wahldividende)$");
type.addBlock(firstRelevantLine);
firstRelevantLine.set(pdfTransaction);

Expand All @@ -161,20 +164,36 @@ private void addDividendeTransaction()
return accountTransaction;
})

// @formatter:off
// Auftragsnummer 623950393
// Reg Shs Healthpeak Pptys Inc
// ISIN US42250P1030
// Valorennummer 50880191
// Zahlungswert USD 0.30
// @formatter:on
.section("name", "currency", "isin", "wkn") //
.find("Auftragsnummer .*") //
.match("^(?<name>.*)$") //
.match("^ISIN (?<isin>[A-Z]{2}[A-Z0-9]{9}[0-9])$") //
.match("^Valorennummer (?<wkn>[A-Z0-9]{5,9})$") //
.match("^Zahlungswert (?<currency>[\\w]{3}) [\\.'\\d]+$") //
.assign((t, v) -> t.setSecurity(getOrCreateSecurity(v)))
.oneOf( //
// @formatter:off
// Auftragsnummer 623950393
// Reg Shs Healthpeak Pptys Inc
// ISIN US42250P1030
// Valorennummer 50880191
// Zahlungswert USD 0.30
// @formatter:on
section -> section //
.attributes("name", "currency", "isin", "wkn") //
.find("Auftragsnummer .*") //
.match("^(?<name>.*)$") //
.match("^ISIN (?<isin>[A-Z]{2}[A-Z0-9]{9}[0-9])$") //
.match("^Valorennummer (?<wkn>[A-Z0-9]{5,9})$") //
.match("^Zahlungswert (?<currency>[\\w]{3}) [\\.'\\d]+$") //
.assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))),
// @formatter:off
// Auftragsnummer XXXXXXXXX
// Reg Shs Pearson PLC
// ISIN GB0006776081
// Zahlungswert GBP 0.07 pro Stück
// @formatter:on
section -> section //
.attributes("name", "currency", "isin", "wkn") //
.find("Auftragsnummer .*") //
.match("^(?<name>.*)$") //
.match("^ISIN (?<isin>[A-Z]{2}[A-Z0-9]{9}[0-9])$") //
.match("^Valorennummer (?<wkn>[A-Z0-9]{5,9})$") //
.match("^Zahlungswert (?<currency>[\\w]{3}) [\\.'\\d]+ pro St.ck$") //
.assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))))

// @formatter:off
// Ihr Bestand per 06.11.2023 25.114744 Stück
Expand Down Expand Up @@ -229,6 +248,49 @@ private void addDividendeTransaction()
addTaxesSectionsTransaction(pdfTransaction, type);
}

private void addAccountStatementTransaction()
{
final DocumentType type = new DocumentType("Kontoauszug in", //
documentContext -> documentContext //
// @formatter:off
// Kontoauszug in EUR 01.12.2023 - 31.12.2023
// @formatter:on
.section("currency") //
.match("^Kontoauszug in (?<currency>[\\w]{3}) .*$") //
.assign((ctx, v) -> ctx.put("currency", asCurrencyCode(v.get("currency")))));

this.addDocumentTyp(type);

// @formatter:off
// Per 31. Dezember 2023
// Abrechnungsperiode 30.09.2023-31.12.2023
// Habenzins 456.60
// @formatter:on
Block interestBlock = new Block("^Per [\\d]{1,2}\\. .* [\\d]{4}$");
type.addBlock(interestBlock);
interestBlock.set(new Transaction<AccountTransaction>()

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

.section("date", "note1", "note2", "amount") //
.documentContext("currency") //
.match("^Per (?<date>[\\d]{1,2}\\. .* [\\d]{4})$") //
.match("^Abrechnungsperiode (?<note1>[\\d]{1,2}\\.[\\d]{2}\\.[\\d]{4})\\-(?<note2>[\\d]{1,2}\\.[\\d]{2}\\.[\\d]{4})$") //
.match("^Habenzins (?<amount>[\\.'\\d]+)$") //
.assign((t, v) -> {
t.setDateTime(asDate(v.get("date")));
t.setCurrencyCode(v.get("currency"));
t.setAmount(asAmount(v.get("amount")));
t.setNote(v.get("note1") + " - " + v.get("note2"));
})

.wrap(TransactionItem::new));
}

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