Skip to content

Commit

Permalink
Modify Zürcher Kantonalbank PDF-Importer to support new transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
Nirus2000 committed Apr 5, 2024
1 parent fe3167b commit a476976
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
PDFBox Version: 1.8.17
Portfolio Performance Version: 0.67.3
-----------------------------------------
Telefon 0844 850 860
Adresse Bahnhofstrasse 9
8001 Zürich
Briefadresse Postfach, 8010 Zürich
Clearing-Nr. 700
BIC (SWIFT) ZKBKCHZZ80A
Depot-Nr. x-xxxx-xxxxxxx
Produkt frankly Sparen 3
Wertschriftendepot
Lautend auf von Manfred Mustermann
Konto-Nr. xxxx-xxxx.xxx
IBAN CHxx xxxx xxxx xxxx xxxx x
Produkt frankly Sparen 3 Konto
Rubrik frankly
Zürich, 29. Februar 2020 Lautend auf von Manfred Mustermann
Abrechnung von Wertschriften
Ausgabe
Abschluss per: 28.12.2023 / Buchungstag: 29.12.2023 / Börsenplatz: Primärmarkt
Abwicklungs-Nr. 123
Auftrags-Nr. 123
Anteile - Units -NT CHF- Swisscanto (CH) IPF III (IPF III) - Swc (CH) IPF III Vorsorge Fonds 95 Passiv
Valor 51215778 / ISIN CH0512157782
Stück CHF CHF
0.645 zu 154.9731 99.96
Net Asset Value (NAV) 154.8362
Total zu Ihren Lasten Valuta 04.01.2024 99.96
Der Unterschied des abgerechneten Preises zum NAV erklärt sich mit Spesen, die gemäss Prospekt zum Schutz der
bestehenden Anleger zu Gunsten des Fondsvermögens erhoben werden.
Automatische Transaktion gemäss Ihrem gewählten Investitionsmodus.
Wir bitten Sie, diese Abrechnung zu kontrollieren und uns allfällige Fehler innert Monatsfrist mitzuteilen. Nach
Ablauf dieser Frist gilt die Abrechnung als genehmigt.
Freundliche Grüsse
Vorsorgestiftung Sparen 3
der Zürcher Kantonalbank
Ohne manuelle Ergänzung
gültig ohne Unterschrift
ANAMHKENFKCIBLAJGKAJCOELENBJGNGNEIGK
AJCJFKEBEOKPNBNMFKAIPGGMIIFOMMGLHCLK
AMJOIBNGKKMHLLLGDKAOADJBJLHIGANLCMOK OY5361 Vorsorgestiftung Sparen 3 der
ACLCKFKGGDGKLMOABKAAMAALNGPEPALBLJHK
ALJKJDAKICIKBKICBKALIDBCBCBDAKACJCBK - Zürcher Kantonalbank
ACAJOCMECBBCMMNDPKAGLLKMILIFIACKPBHK
ACLPLPFEOFEICILMMKAMOOJMGOJMGLFEPMIK
AGLDJFLKLPBGGGHDGKACBMCENECNDOODDHFK 1/1
AIKAACIGMEEGAAMIKKAGOEIAGCMKCOCGAMGK
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
PDFBox Version: 1.8.17
Portfolio Performance Version: 0.67.3
-----------------------------------------
Ph^0Tr7E538%8{4201069824}
Kontakt frankly Support
Telefon 0800 320 000
Briefadresse Postfach, 8010 Zürich
P.P. A Postfach 715, 8010 Zürich Post CH AG
Herr
Manfred Mustermann
Musterstrasse 16
1111 City
Baden, 31. Mai 2021
Abrechnung von Wertschriften
Konto: xxxx-xxxx.xxx, lautend auf: Manfred Mustermann
Wir haben die folgende Transaktion für Sie getätigt:
Zeichnungen
Valor Bezeichnung Anzahl Kurs CHF Datum Total CHF
51215778 SWC (CH) IPF III VF 95 Passiv NT 0.633 157.95 28.05.2021 99.98
Total Belastung Zeichnungen 99.98
Im Kurs sind Ausgabespesen zu Gunsten Fonds (bei Zeichnungen) resp. Rücknahmespesen zu Gunsten Fonds
enthalten. Diese Spesen werden zum Schutz der bestehenden Investoren dem Fondsvermögen gutgeschrieben.
Wir bitten Sie, diese Abrechnung zu kontrollieren und uns allfällige Fehler innert Monatsfrist mitzuteilen. Nach
Ablauf dieser Frist gilt die Abrechnung als genehmigt.
Freundliche Grüsse
Vorsorgestiftung Sparen 3
der Zürcher Kantonalbank
Ohne manuelle Ergänzung
Gültig ohne Unterschrift
Vorsorgestiftung Sparen 3
der Zürcher Kantonalbank
1/1
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,68 @@ public void testWertpapierKauf04WithSecurityInCHF()
}))));
}

@Test
public void testWertpapierKauf05()
{
ZuercherKantonalbankPDFExtractor extractor = new ZuercherKantonalbankPDFExtractor(new Client());

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

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

assertThat(errors, empty());
assertThat(countSecurities(results), is(1L));
assertThat(countBuySell(results), is(1L));
assertThat(countAccountTransactions(results), is(0L));
assertThat(results.size(), is(2));
new AssertImportActions().check(results, "CHF");

// check security
assertThat(results, hasItem(security( //
hasIsin("CH0512157782"), hasWkn("51215778"), hasTicker(null), //
hasName("Swisscanto (CH) IPF III (IPF III)"), //
hasCurrencyCode("CHF"))));

// check buy sell transaction
assertThat(results, hasItem(purchase( //
hasDate("2023-12-28T00:00"), hasShares(0.645), //
hasSource("Kauf05.txt"), //
hasNote("Abwicklungs-Nr. 123 | Auftrags-Nr. 123"), //
hasAmount("CHF", 99.96), hasGrossValue("CHF", 99.96), //
hasTaxes("CHF", 0.00), hasFees("CHF", 0.00))));
}

@Test
public void testWertpapierKauf06()
{
ZuercherKantonalbankPDFExtractor extractor = new ZuercherKantonalbankPDFExtractor(new Client());

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

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

assertThat(errors, empty());
assertThat(countSecurities(results), is(1L));
assertThat(countBuySell(results), is(1L));
assertThat(countAccountTransactions(results), is(0L));
assertThat(results.size(), is(2));
new AssertImportActions().check(results, "CHF");

// check security
assertThat(results, hasItem(security( //
hasIsin(null), hasWkn("51215778"), hasTicker(null), //
hasName("SWC (CH) IPF III VF 95 Passiv NT"), //
hasCurrencyCode("CHF"))));

// check buy sell transaction
assertThat(results, hasItem(purchase( //
hasDate("2021-05-28T00:00"), hasShares(0.633), //
hasSource("Kauf06.txt"), //
hasNote(null), //
hasAmount("CHF", 99.98), hasGrossValue("CHF", 99.98), //
hasTaxes("CHF", 0.00), hasFees("CHF", 0.00))));
}

@Test
public void testWertpapierVerkauf01()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ public String getLabel()

private void addBuySellTransaction()
{
final DocumentType type = new DocumentType("Ihr (Kauf|Verkauf)");
final DocumentType type = new DocumentType("(Ihr (Kauf|Verkauf)|Abrechnung von Wertschriften)");
this.addDocumentTyp(type);

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

Block firstRelevantLine = new Block("^Gesch.fts\\-Nr\\..*$");
Block firstRelevantLine = new Block("^(Gesch.fts\\-Nr\\..*|Ausgabe|Zeichnungen)$");
type.addBlock(firstRelevantLine);
firstRelevantLine.set(pdfTransaction);

Expand All @@ -74,34 +74,74 @@ private void addBuySellTransaction()
t.setType(PortfolioTransaction.Type.SELL);
})

// @formatter:off
// Abwicklungs-Nr. 592473551 / Auftrags-Nr. ONBA-0005128022772021
// Registered Shs Glencore PLC
// Valor 12964057 / ISIN JE00B4T3BW64
// Stück GBP GBP
// @formatter:on
.section("name", "wkn", "isin", "currency")
.find("Abwicklungs\\-Nr\\..*")
.match("^(?<name>.*)$")
.match("Valor (?<wkn>[A-Z0-9]{5,9}) \\/ ISIN (?<isin>[A-Z]{2}[A-Z0-9]{9}[0-9])$")
.match("^St.ck (?<currency>[\\w]{3}) [\\w]{3}$")
.assign((t, v) -> t.setSecurity(getOrCreateSecurity(v)))
.oneOf( //
// @formatter:off
// Zeichnungen
// Valor Bezeichnung Anzahl Kurs CHF Datum Total CHF
// 51215778 SWC (CH) IPF III VF 95 Passiv NT 0.633 157.95 28.05.2021 99.98
// @formatter:on
section -> section //
.attributes("currency", "wkn", "name") //
.match("^Valor Bezeichnung Anzahl Kurs (?<currency>[\\w]{3}) Datum Total [\\w]{3}$")
.match("^(?<wkn>[A-Z0-9]{5,9}) (?<name>.*) [\\.'\\d]+ [\\.'\\d]+ [\\d]{2}\\.[\\d]{2}\\.[\\d]{4} [\\.'\\d]+$")
.assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))),
// @formatter:off
// Anteile - Units -NT CHF- Swisscanto (CH) IPF III (IPF III) - Swc (CH) IPF III Vorsorge Fonds 95 Passiv
// Valor 51215778 / ISIN CH0512157782
// Stück GBP GBP
// @formatter:on
section -> section //
.attributes("currency", "name", "wkn", "isin") //
.match("^Anteile .* (?<currency>[\\w]{3})\\- (?<name>.*) \\-.*$")
.match("Valor (?<wkn>[A-Z0-9]{5,9}) \\/ ISIN (?<isin>[A-Z]{2}[A-Z0-9]{9}[0-9])$")
.assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))),
// @formatter:off
// Abwicklungs-Nr. 592473551 / Auftrags-Nr. ONBA-0005128022772021
// Registered Shs Glencore PLC
// Valor 12964057 / ISIN JE00B4T3BW64
// Stück GBP GBP
// @formatter:on
section -> section //
.attributes("name", "wkn", "isin", "currency") //
.find("Abwicklungs\\-Nr\\..*")
.match("^(?<name>.*)$")
.match("Valor (?<wkn>[A-Z0-9]{5,9}) \\/ ISIN (?<isin>[A-Z]{2}[A-Z0-9]{9}[0-9])$")
.match("^St.ck (?<currency>[\\w]{3}) [\\w]{3}$")
.assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))))

// @formatter:off
// Stück GBP GBP
// 1'000 zu 3.545 3'545.00
// @formatter:on
.section("shares") //
.find("St.ck [\\w]{3} [\\w]{3}") //
.match("^(?<shares>[\\.'\\d]+) zu [\\.'\\d]+ [\\.'\\d]+$") //
.assign((t, v) -> t.setShares(asShares(v.get("shares"))))
.oneOf( //
// @formatter:off
// 51215778 SWC (CH) IPF III VF 95 Passiv NT 0.633 157.95 28.05.2021 99.98
// @formatter:on
section -> section //
.attributes("shares") //
.match("^[A-Z0-9]{5,9} .* (?<shares>[\\.'\\d]+) [\\.'\\d]+ [\\d]{2}\\.[\\d]{2}\\.[\\d]{4} [\\.'\\d]+$") //
.assign((t, v) -> t.setShares(asShares(v.get("shares")))),
// @formatter:off
// Stück GBP GBP
// 1'000 zu 3.545 3'545.00
// @formatter:on
section -> section //
.attributes("shares") //
.find("St.ck [\\w]{3} [\\w]{3}") //
.match("^(?<shares>[\\.'\\d]+) zu [\\.'\\d]+ [\\.'\\d]+$") //
.assign((t, v) -> t.setShares(asShares(v.get("shares")))))

// @formatter:off
// Abschluss per: 04.10.2021 / Buchungstag: 04.10.2021 / Börsenplatz: LSE UK 1 CUR
// @formatter:on
.section("date") //
.match("^Abschluss per: (?<date>[\\d]{2}\\.[\\d]{2}\\.[\\d]{4}).*$") //
.assign((t, v) -> t.setDate(asDate(v.get("date"))))
.oneOf( //
// @formatter:off
// Abschluss per: 04.10.2021 / Buchungstag: 04.10.2021 / Börsenplatz: LSE UK 1 CUR
// @formatter:on
section -> section //
.attributes("date") //
.match("^Abschluss per: (?<date>[\\d]{2}\\.[\\d]{2}\\.[\\d]{4}).*$") //
.assign((t, v) -> t.setDate(asDate(v.get("date")))),
// @formatter:off
// 51215778 SWC (CH) IPF III VF 95 Passiv NT 0.633 157.95 28.05.2021 99.98
// @formatter:on
section -> section //
.attributes("date") //
.match("^[A-Z0-9]{5,9} .* [\\.'\\d]+ [\\.'\\d]+ (?<date>[\\d]{2}\\.[\\d]{2}\\.[\\d]{4}) [\\.'\\d]+$") //
.assign((t, v) -> t.setDate(asDate(v.get("date")))))

.oneOf( //
// @formatter:off
Expand All @@ -117,6 +157,18 @@ private void addBuySellTransaction()
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
}),
// @formatter:off
// Valor Bezeichnung Anzahl Kurs CHF Datum Total CHF
// Total Belastung Zeichnungen 99.98
// @formatter:on
section -> section //
.attributes("currency", "amount") //
.match("^Valor Bezeichnung Anzahl Kurs [\\w]{3} Datum Total (?<currency>[\\w]{3})$")
.match("^Total Belastung Zeichnungen (?<amount>[\\.'\\d]+)$") //
.assign((t, v) -> {
t.setAmount(asAmount(v.get("amount")));
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
}),
// @formatter:off
// Stück CHF CHF
// Total zu Ihren Lasten Valuta 10.06.2022 7'294.30
// Total zu Ihren Gunsten Valuta 28.02.2023 8'512.11
Expand Down Expand Up @@ -157,19 +209,37 @@ private void addBuySellTransaction()
type.getCurrentContext().put("fxCurrency", rate.getBaseCurrency());
})

// @formatter:off
// Abwicklungs-Nr. 592473551 / Auftrags-Nr. ONBA-0005128022772021
// @formatter:on
.section("note").optional() //
.match("^(?<note>Abwicklungs\\-Nr\\..*) \\/.*$") //
.assign((t, v) -> t.setNote(trim(v.get("note"))))
.optionalOneOf( //
// @formatter:off
// Abwicklungs-Nr. 592473551 / Auftrags-Nr. ONBA-0005128022772021
// @formatter:on
section -> section //
.attributes("note") //
.match("^(?<note>Abwicklungs\\-Nr\\..*) \\/.*$") //
.assign((t, v) -> t.setNote(trim(v.get("note")))),
// @formatter:off
// Abwicklungs-Nr. 123
// @formatter:on
section -> section //
.attributes("note") //
.match("^(?<note>Abwicklungs\\-Nr\\..*)$") //
.assign((t, v) -> t.setNote(trim(v.get("note")))))

// @formatter:off
// Abwicklungs-Nr. 592473551 / Auftrags-Nr. ONBA-0005128022772021
// @formatter:on
.section("note").optional() //
.match("^.* \\/ (?<note>Auftrags\\-Nr\\..*)$") //
.assign((t, v) -> t.setNote(concatenate(t.getNote(), trim(v.get("note")), " | ")))
.optionalOneOf( //
// @formatter:off
// Abwicklungs-Nr. 592473551 / Auftrags-Nr. ONBA-0005128022772021
// @formatter:on
section -> section //
.attributes("note") //
.match("^.* \\/ (?<note>Auftrags\\-Nr\\..*)$") //
.assign((t, v) -> t.setNote(concatenate(t.getNote(), trim(v.get("note")), " | "))),
// @formatter:off
// Abwicklungs-Nr. 123
// @formatter:on
section -> section //
.attributes("note") //
.match("^(?<note>Auftrags\\-Nr\\..*)$") //
.assign((t, v) -> t.setNote(concatenate(t.getNote(), trim(v.get("note")), " | "))))

.conclude(ExtractorUtils.fixGrossValueBuySell())

Expand Down

0 comments on commit a476976

Please sign in to comment.