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

Added Scalable Capital importer #4410

Merged
merged 1 commit into from
Dec 22, 2024
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,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
Original file line number Diff line number Diff line change
@@ -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<Exception> errors = new ArrayList<>();

List<Item> 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))));

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ public PDFImportAssistant(Client client, List<File> 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));
Expand Down
Original file line number Diff line number Diff line change
@@ -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<BuySellEntry>()

.subject(() -> {
BuySellEntry portfolioTransaction = new BuySellEntry();
portfolioTransaction.setType(PortfolioTransaction.Type.BUY);
return portfolioTransaction;
})

.section("name", "currency", "isin") //
.find("Typ Wertpapier Anzahl Kurs Betrag") //
.match("^Kauf (?<name>.*) " //
+ "[.,\\d]+ Stk. " //
+ "[.,\\d]+ [A-Z]{3} " //
+ "[.,\\d]+ (?<currency>[A-Z]{3})")
.match("^(?<isin>[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 .* " //
+ "(?<shares>[.,\\d]+) Stk. " //
+ "[.,\\d]+ [A-Z]{3} " //
+ "(?<amount>[.,\\d]+) (?<currency>[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 (?<date>[\\d]{2}\\.[\\w]{2}\\.[\\d]{4}) " //
+ "(?<time>[\\d]{2}:[\\d]{2}:[\\d]{2}) .*$")
.assign((t, v) -> t.setDate(asDate(v.get("date"), v.get("time"))))

.section("fee", "currency") //
.optional() //
.match("^Ordergebühren \\+(?<fee>[.,\\d]+) (?<currency>[A-Z]{3})$").assign((t, v) -> {
var currency = asCurrencyCode(v.get("currency"));
var fee = asAmount(v.get("fee"));
var tx = t.getPortfolioTransaction();

tx.addUnit(new Unit(Unit.Type.FEE, Money.of(currency, fee)));
t.setAmount(tx.getAmount() + fee);
})

.wrap(e -> e.getPortfolioTransaction().getSecurity() == null ? null : new BuySellEntryItem(e)));
}

}
Loading