diff --git a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/scalablecapital/Kauf01.txt b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/scalablecapital/Kauf01.txt new file mode 100644 index 0000000000..f70a8767d9 --- /dev/null +++ b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/scalablecapital/Kauf01.txt @@ -0,0 +1,27 @@ +PDFBox Version: 1.8.17 +Portfolio Performance Version: 0.72.2 +----------------------------------------- +Scalable Capital GmbH • Seitzstraße 8e • 80538 München • Deutschland +gMbdE BSBVSo +PIUWUpOPvMUGNZ OXxLwz 66 +80481 YHbanuVrhilpy Datum 12.12.2024 +Deutschland Seite 1 / 1 +Wertpapierabrechnung +für Kundenauftrag +Typ LIMIT Order SCALsin78vS5CYz +Ausführung 12.12.2024 13:12:51 Geschäft 36581526 +Ausführungsplatz EIX Lagerland Luxemburg +Depot 1970027444 Verwahrart Wertpapierrechnung +Wertpapierabrechnung +Typ Wertpapier Anzahl Kurs Betrag +Kauf Vngrd Fds-ESG Dv.As-Pc Al ETF 3,00 Stk. 6,168 EUR 18,50 EUR +IE0008T6IUX0 +Ordergebühren +0,99 EUR +Total 19,49 EUR +Der Betrag wird mit dem Verrechnungskonto DE168836967200035482353 (Valuta: 16.12.2024) verrechnet. +Bitte überprüfen Sie die Informationen auf Richtigkeit und melden Sie etwaige Einwände unverzüglich bei uns. +Verwenden Sie dafür den Menüpunkt Support im Kundenbereich. +Scalable Capital GmbH HRB 217778 Geschäftsführer: Aufsichtsrat: Seite +Seitzstraße 8e Amtsgericht München Erik Podzuweit, Florian Prucker Patrick Olson (Vorsitzender) +80538 München USt.-Id. Nr.: DE300434774 Martin Krebs, Dirk Franzmeyer, Dirk +Urmoneit 1 / 1 diff --git a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/scalablecapital/ScalableCapitalPDFExtractorTest.java b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/scalablecapital/ScalableCapitalPDFExtractorTest.java new file mode 100644 index 0000000000..6362047f9a --- /dev/null +++ b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/scalablecapital/ScalableCapitalPDFExtractorTest.java @@ -0,0 +1,70 @@ +package name.abuchen.portfolio.datatransfer.pdf.scalablecapital; + +import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasAmount; +import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasCurrencyCode; +import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasDate; +import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasFees; +import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasGrossValue; +import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasIsin; +import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasName; +import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasNote; +import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasShares; +import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasSource; +import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasTaxes; +import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.purchase; +import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.security; +import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countAccountTransactions; +import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countBuySell; +import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countSecurities; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsEmptyCollection.empty; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import name.abuchen.portfolio.datatransfer.Extractor.Item; +import name.abuchen.portfolio.datatransfer.actions.AssertImportActions; +import name.abuchen.portfolio.datatransfer.pdf.PDFInputFile; +import name.abuchen.portfolio.datatransfer.pdf.ScalableCapitalPDFExtractor; +import name.abuchen.portfolio.model.Client; +import name.abuchen.portfolio.money.CurrencyUnit; + +@SuppressWarnings("nls") +public class ScalableCapitalPDFExtractorTest +{ + @Test + public void testKauf01() + { + ScalableCapitalPDFExtractor extractor = new ScalableCapitalPDFExtractor(new Client()); + + List errors = new ArrayList<>(); + + List results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Kauf01.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, CurrencyUnit.EUR); + + // check security + assertThat(results, hasItem(security( // + hasIsin("IE0008T6IUX0"), // + hasName("Vngrd Fds-ESG Dv.As-Pc Al ETF"), // + hasCurrencyCode("EUR")))); + + // check dividends transaction + assertThat(results, hasItem(purchase( // + hasDate("2024-12-12T13:12:51"), hasShares(3), // + hasSource("Kauf01.txt"), // + hasNote(null), // + hasAmount("EUR", 19.49), hasGrossValue("EUR", 18.50), // + hasTaxes("EUR", 0.00), hasFees("EUR", 0.99)))); + + } +} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/PDFImportAssistant.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/PDFImportAssistant.java index c4763e8942..f9c085a306 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/PDFImportAssistant.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/PDFImportAssistant.java @@ -105,6 +105,7 @@ public PDFImportAssistant(Client client, List files) extractors.add(new SaxoBankPDFExtractor(client)); extractors.add(new SberbankEuropeAGPDFExtractor(client)); extractors.add(new SBrokerPDFExtractor(client)); + extractors.add(new ScalableCapitalPDFExtractor(client)); extractors.add(new ScorePriorityIncPDFExtractor(client)); extractors.add(new SelfWealthPDFExtractor(client)); extractors.add(new SimpelPDFExtractor(client)); diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/ScalableCapitalPDFExtractor.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/ScalableCapitalPDFExtractor.java new file mode 100644 index 0000000000..1a7b799a2d --- /dev/null +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/ScalableCapitalPDFExtractor.java @@ -0,0 +1,86 @@ +package name.abuchen.portfolio.datatransfer.pdf; + +import name.abuchen.portfolio.datatransfer.pdf.PDFParser.Block; +import name.abuchen.portfolio.datatransfer.pdf.PDFParser.DocumentType; +import name.abuchen.portfolio.datatransfer.pdf.PDFParser.Transaction; +import name.abuchen.portfolio.model.BuySellEntry; +import name.abuchen.portfolio.model.Client; +import name.abuchen.portfolio.model.PortfolioTransaction; +import name.abuchen.portfolio.model.Transaction.Unit; +import name.abuchen.portfolio.money.Money; + +@SuppressWarnings("nls") +public class ScalableCapitalPDFExtractor extends AbstractPDFExtractor +{ + public ScalableCapitalPDFExtractor(Client client) + { + super(client); + + addBankIdentifier("Scalable Capital GmbH"); + + addPurchaseTransaction(); + } + + @Override + public String getLabel() + { + return "Scalable Capital"; + } + + private void addPurchaseTransaction() + { + final DocumentType type = new DocumentType("Wertpapierabrechnung"); + + this.addDocumentTyp(type); + + Block purchase = new Block(".* Kundenauftrag.*"); + type.addBlock(purchase); + purchase.set(new Transaction() + + .subject(() -> { + BuySellEntry portfolioTransaction = new BuySellEntry(); + portfolioTransaction.setType(PortfolioTransaction.Type.BUY); + return portfolioTransaction; + }) + + .section("name", "currency", "isin") // + .find("Typ Wertpapier Anzahl Kurs Betrag") // + .match("^Kauf (?.*) " // + + "[.,\\d]+ Stk. " // + + "[.,\\d]+ [A-Z]{3} " // + + "[.,\\d]+ (?[A-Z]{3})") + .match("^(?[A-Z]{2}[A-Z0-9]{9}[0-9])$") // + .assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))) + + .section("shares", "amount", "currency") // + .find("Typ Wertpapier Anzahl Kurs Betrag") // + .match("^Kauf .* " // + + "(?[.,\\d]+) Stk. " // + + "[.,\\d]+ [A-Z]{3} " // + + "(?[.,\\d]+) (?[A-Z]{3})") + .assign((t, v) -> { + t.setCurrencyCode(asCurrencyCode(v.get("currency"))); + t.setAmount(asAmount(v.get("amount"))); + t.setShares(asShares(v.get("shares"))); + }) + + .section("date", "time") // + .match("^Ausführung (?[\\d]{2}\\.[\\w]{2}\\.[\\d]{4}) " // + + "(?