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 Zürcher Kantonalbank PDF-Importer to support new transaction #3918

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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading