package name.abuchen.portfolio.datatransfer.pdf;
import java.io.IOException;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.AccountTransaction;
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;
public class FlatexPDFExtractor extends AbstractPDFExtractor
{
public FlatexPDFExtractor(Client client) throws IOException
{
super(client);
addBuySellTransaction();
addBuyTransaction();
addDepositAndWithdrawalTransaction();
addDividendTransaction();
addSellTransaction();
addTransferInTransaction();
addTransferOutTransaction();
addRemoveTransaction();
addRemoveNewFormatTransaction();
addOverdraftinterestTransaction();
addTaxoptimisationTransaction();
}
@SuppressWarnings("nls")
private void addBuySellTransaction()
{
DocumentType type = new DocumentType("Sammelabrechnung (Wertpapierkauf/-verkauf)");
this.addDocumentTyp(type);
Block block = new Block("Nr.(\\d*)/(\\d*) Kauf.*");
type.addBlock(block);
block.set(new Transaction<BuySellEntry>().subject(() -> {
BuySellEntry entry = new BuySellEntry();
entry.setType(PortfolioTransaction.Type.BUY);
return entry;
})
.section("wkn", "isin", "name")
.match("Nr.[0-9A-Za-z]*/(\\d*) Kauf *(?<name>[^(]*) \\((?<isin>[^/]*)/(?<wkn>[^)]*)\\)")
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("shares", "date")
.match("^davon ausgef\\. *: (?<shares>[.\\d]+,\\d*) St\\. *Schlusstag *: *(?<date>\\d+\\.\\d+\\.\\d{4}+), \\d+:\\d+ Uhr")
.assign((t, v) -> {
t.setShares(asShares(v.get("shares")));
t.setDate(asDate(v.get("date")));
})
.section("amount", "currency") //
.match(".* Endbetrag *: *(?<amount>[\\d.-]+,\\d+) (?<currency>\\w{3}+)") //
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(asAmount(v.get("amount")));
})
.section("fee", "currency").optional() //
.match(".* Provision *: *(?<fee>[\\d.-]+,\\d+) (?<currency>\\w{3}+)") //
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional() //
.match(".* Eigene Spesen *: *(?<fee>[\\d.-]+,\\d+) (?<currency>\\w{3}+)") //
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional() //
.match(".* \\*Fremde Spesen *: *(?<fee>[\\d.-]+,\\d+) (?<currency>\\w{3}+)") //
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.wrap(t -> new BuySellEntryItem(t)));
block = new Block("Nr.(\\d*)/(\\d*) Verkauf.*");
type.addBlock(block);
block.set(new Transaction<BuySellEntry>().subject(() -> {
BuySellEntry entry = new BuySellEntry();
entry.setType(PortfolioTransaction.Type.SELL);
return entry;
})
.section("wkn", "isin", "name")
.match("Nr.(\\d*)/(\\d*) Verkauf *(?<name>[^(]*) \\((?<isin>[^/]*)/(?<wkn>[^)]*)\\)")
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("shares", "date")
.match("^davon ausgef\\. *: (?<shares>[.\\d]+,\\d*) St\\. *Schlusstag *: *(?<date>\\d+.\\d+.\\d{4}+), \\d+:\\d+ Uhr")
.assign((t, v) -> {
t.setShares(asShares(v.get("shares")));
t.setDate(asDate(v.get("date")));
})
.section("amount", "currency") //
.match(".* Endbetrag *: *(?<amount>[\\d.-]+,\\d+) (?<currency>\\w{3}+)") //
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(asAmount(v.get("amount")));
})
.section("fee", "currency").optional() //
.match(".* Provision *: *(?<fee>[\\d.-]+,\\d+) (?<currency>\\w{3}+)") //
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional() //
.match(".* Eigene Spesen *: *(?<fee>[\\d.-]+,\\d+) (?<currency>\\w{3}+)") //
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional() //
.match(".* \\*Fremde Spesen *: *(?<fee>[\\d.-]+,\\d+) (?<currency>\\w{3}+)") //
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("tax", "currency").optional() //
.match(".* \\*\\*Einbeh. Steuer *: *(?<tax>[\\d.]+,\\d+) (?<currency>\\w{3}+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.TAX,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("tax"))))))
.section("taxreturn", "currency")
.optional()
.match(".* \\*\\*Einbeh. Steuer *: *(?<taxreturn>-[\\d.]+,\\d+) (?<currency>\\w{3}+)")
.assign((t, v) -> {
t.setAmount(t.getPortfolioTransaction().getAmount() - asAmount(v.get("taxreturn")));
})
.wrap(t -> new BuySellEntryItem(t)));
addTaxReturnBlock(type);
}
@SuppressWarnings("nls")
private void addBuyTransaction()
{
DocumentType type = new DocumentType("Wertpapierabrechnung Kauf Fonds/Zertifikate");
this.addDocumentTyp(type);
Block block = new Block(" *biw AG *");
type.addBlock(block);
block.set(new Transaction<BuySellEntry>().subject(() -> {
BuySellEntry entry = new BuySellEntry();
entry.setType(PortfolioTransaction.Type.BUY);
return entry;
})
.section("date").match(".*Schlusstag *(?<date>\\d+.\\d+.\\d{4}).*") //
.assign((t, v) -> t.setDate(asDate(v.get("date"))))
.section("wkn", "isin", "name")
.match("Nr.[0-9A-Za-z]*/(\\d*) Kauf *(?<name>[^(]*) \\((?<isin>[^/]*)/(?<wkn>[^)]*)\\)") //
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("shares") //
.match("^Ausgeführt *(?<shares>[\\.\\d]+(,\\d*)?) *St\\..*") //
.assign((t, v) -> t.setShares(asShares(v.get("shares"))))
.section("amount", "currency") //
.match(" * Endbetrag *(?<currency>\\w{3}+) *(?<amount>[\\d.-]+,\\d+)") //
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(asAmount(v.get("amount")));
})
.section("fee", "currency").optional()
//
.match(".* Provision *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional()
//
.match(".* Eigene Spesen *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional()
//
.match(".* \\*Fremde Spesen *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.wrap(t -> new BuySellEntryItem(t)));
}
@SuppressWarnings("nls")
private void addDepositAndWithdrawalTransaction()
{
final DocumentType type = new DocumentType("Kontoauszug Nr:", (context, lines) -> {
Pattern pYear = Pattern.compile("Kontoauszug Nr:[ ]*\\d+/(\\d+).*");
Pattern pCurrency = Pattern.compile("Kontow.hrung:[ ]+(\\w{3}+)");
// read the current context here
for (String line : lines)
{
Matcher m = pYear.matcher(line);
if (m.matches())
{
context.put("year", m.group(1));
}
m = pCurrency.matcher(line);
if (m.matches())
{
context.put("currency", m.group(1));
}
}
});
this.addDocumentTyp(type);
// deposit, add value to account
// 01.01. 01.01. xyz 123,45+
Block block = new Block("\\d+\\.\\d+\\.[ ]+\\d+\\.\\d+\\.[ ]+.berweisung[ ]+[\\d.-]+,\\d+[+-]");
type.addBlock(block);
block.set(new Transaction<AccountTransaction>().subject(() -> {
AccountTransaction t = new AccountTransaction();
t.setType(AccountTransaction.Type.DEPOSIT);
return t;
})
.section("valuta", "amount", "sign")
.match("\\d+.\\d+.[ ]+(?<valuta>\\d+.\\d+.)[ ]+.berweisung[ ]+(?<amount>[\\d.-]+,\\d+)(?<sign>[+-])")
.assign((t, v) -> {
Map<String, String> context = type.getCurrentContext();
String date = v.get("valuta");
if (date != null)
{
// create a long date from the year in the
// context
t.setDate(asDate(date + context.get("year")));
}
t.setNote(v.get("text"));
t.setAmount(asAmount(v.get("amount")));
t.setCurrencyCode(asCurrencyCode(context.get("currency")));
//check for withdrawals
String sign=v.get("sign");
if("-".equals(sign))
{
//change type for withdrawals
t.setType(AccountTransaction.Type.REMOVAL);
}
}).wrap(t -> new TransactionItem(t)));
// fees for foreign dividends, subtract value from account
block = new Block("\\d+\\.\\d+\\.[ ]+\\d+\\.\\d+\\.[ ]+Geb.hr Kapitaltransaktion Ausland[ ]+[\\d.-]+,\\d+[-]");
type.addBlock(block);
block.set(new Transaction<AccountTransaction>().subject(() -> {
AccountTransaction t = new AccountTransaction();
t.setType(AccountTransaction.Type.FEES);
return t;
})
.section("valuta", "amount")
.match("\\d+.\\d+.[ ]+(?<valuta>\\d+.\\d+.)[ ]+Geb.hr Kapitaltransaktion Ausland[ ]+(?<amount>[\\d.-]+,\\d+)[-]")
.assign((t, v) -> {
Map<String, String> context = type.getCurrentContext();
String date = v.get("valuta");
if (date != null)
{
// create a long date from the year in the
// context
t.setDate(asDate(date + context.get("year")));
}
t.setNote(v.get("text"));
t.setAmount(asAmount(v.get("amount")));
t.setCurrencyCode(asCurrencyCode(context.get("currency")));
}).wrap(t -> new TransactionItem(t)));
}
@SuppressWarnings("nls")
private void addDividendTransaction()
{
DocumentType type1 = new DocumentType("Dividendengutschrift");
DocumentType type2 = new DocumentType("Ertragsmitteilung");
DocumentType type3 = new DocumentType("Zinsgutschrift");
this.addDocumentTyp(type1);
this.addDocumentTyp(type2);
this.addDocumentTyp(type3);
Block block = new Block("Ihre Depotnummer.*");
type1.addBlock(block);
type2.addBlock(block);
type3.addBlock(block);
block.set(new Transaction<AccountTransaction>()
//
.subject(() -> {
AccountTransaction t = new AccountTransaction();
t.setType(AccountTransaction.Type.DIVIDENDS);
return t;
})
.section("wkn", "isin", "name")
.match("Nr\\.(\\d*) * (?<name>[^(]*) *\\((?<isin>[^/]*)/(?<wkn>[^)]*)\\)").assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("shares") //
.match("^St\\.[^:]+: *(?<shares>[\\.\\d]+(,\\d*)?).*")
.assign((t, v) -> t.setShares(asShares(v.get("shares"))))
.section("amount", "currency") //
.match(".* Endbetrag *: *(?<amount>[\\d.-]+,\\d+) (?<currency>\\w{3}+)") //
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(asAmount(v.get("amount")));
})
.section("date") //
.match("Valuta * : *(?<date>\\d+.\\d+.\\d{4}+).*")
.assign((t, v) -> t.setDate(asDate(v.get("date"))))
.wrap(t -> new TransactionItem(t)));
}
@SuppressWarnings("nls")
private void addSellTransaction()
{
DocumentType type = new DocumentType("Wertpapierabrechnung Verkauf");
this.addDocumentTyp(type);
Block block = new Block(" *biw AG *");
type.addBlock(block);
block.set(new Transaction<BuySellEntry>().subject(() -> {
BuySellEntry entry = new BuySellEntry();
entry.setType(PortfolioTransaction.Type.SELL);
return entry;
})
.section("date").match(".*Valuta *(?<date>\\d+.\\d+.\\d{4}).*") //
.assign((t, v) -> t.setDate(asDate(v.get("date"))))
.section("wkn", "isin", "name")
.match("Nr.(\\d*)/(\\d*) Verkauf *(?<name>[^(]*) \\((?<isin>[^/]*)/(?<wkn>[^)]*)\\)") //
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("shares", "notation")
.match("^Ausgeführt *(?<shares>[\\.\\d]+(,\\d*)?) *(?<notation>St\\.|\\w{3}+).*") //
.assign((t, v) -> {
String notation = v.get("notation");
if (notation != null && !notation.equalsIgnoreCase("St."))
{
// Prozent-Notierung, Workaround..
t.setShares((asShares(v.get("shares")) / 100));
}
else
{
t.setShares(asShares(v.get("shares")));
}
})
.section("amount", "currency")
.match(".* Endbetrag(\\s+)(?<currency>\\w{3}+)(\\s+)(?<amount>[\\d.]+,\\d+)")
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(asAmount(v.get("amount")));
})
.section("fee", "currency").optional()
//
.match(".* Provision *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional()
//
.match(".* Eigene Spesen *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional()
//
.match(".* \\*Fremde Spesen *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.wrap(t -> new BuySellEntryItem(t)));
}
@SuppressWarnings("nls")
private void addTransferInTransaction()
{
DocumentType type = new DocumentType("Depoteingang");
this.addDocumentTyp(type);
Block block = new Block(" *biw AG *");
type.addBlock(block);
block.set(new Transaction<PortfolioTransaction>().subject(() -> {
PortfolioTransaction entry = new PortfolioTransaction();
entry.setType(PortfolioTransaction.Type.DELIVERY_INBOUND);
return entry;
})
.section("date").match("Datum(\\s*):(\\s+)(?<date>\\d+.\\d+.\\d{4})") //
.assign((t, v) -> t.setDate(asDate(v.get("date"))))
.section("isin", "name")
.match("Depoteingang *(?<name>[^(]*) \\((?<isin>[^/]*)\\)") //
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("shares", "notation")
.match("^Stk\\.\\/Nominale(\\s*):(\\s+)(?<shares>[\\.\\d]+(,\\d*)?) *(?<notation>St\\.|\\w{3}+)(.*)") //
.assign((t, v) -> {
String notation = v.get("notation");
if (notation != null && !notation.equalsIgnoreCase("Stk"))
{
// Prozent-Notierung, Workaround..
t.setShares((asShares(v.get("shares")) / 100));
}
else
{
t.setShares(asShares(v.get("shares")));
}
})
.section("rate", "currency")
.match("^Kurs(\\s*):(\\s+)(?<rate>[\\d.]+,\\d+)(\\s+)(?<currency>\\w{3}+)(.*)")
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount((asAmount(v.get("rate")) * t.getShares())/100/100/100); //TODO/Workaround: Die Abrechnung ist hier komisch, der Kurs wird in der Beispiel Datei in EUR angegeben, müsste aber eigentlich in Prozent sein...
})
.section("fee", "currency").optional()
//
.match(".* Provision *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional()
//
.match(".* Eigene Spesen *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional()
//
.match(".* \\*Fremde Spesen *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.wrap(t -> new TransactionItem(t)));
}
@SuppressWarnings("nls")
private void addTransferOutTransaction()
{
DocumentType type = new DocumentType("Depotausgang");
this.addDocumentTyp(type);
Block block = new Block("Depotausgang(.*)");
type.addBlock(block);
block.set(new Transaction<BuySellEntry>().subject(() -> {
BuySellEntry entry = new BuySellEntry();
entry.setType(PortfolioTransaction.Type.SELL);
return entry;
})
.section("date").match("Fälligkeitstag(\\s*):(\\s+)(?<date>\\d+.\\d+.\\d{4})(.*)") //
.assign((t, v) -> t.setDate(asDate(v.get("date"))))
.section("isin", "name")
.match("Depotausgang *(?<name>[^(]*) \\((?<isin>[^/]*)\\)") //
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("shares", "notation")
.match("^Stk\\.\\/Nominale(\\s*):(\\s+)(?<shares>[\\.\\d]+(,\\d*)?) *(?<notation>St\\.|\\w{3}+)(.*)") //
.assign((t, v) -> {
String notation = v.get("notation");
if (notation != null && !notation.equalsIgnoreCase("Stk"))
{
// Prozent-Notierung, Workaround..
t.setShares((asShares(v.get("shares")) / 100));
}
else
{
t.setShares(asShares(v.get("shares")));
}
})
.section("amount", "currency")
.match("(.*)Geldgegenwert\\*\\*(.*)(\\s*):(\\s*)(?<amount>[\\d.]+,\\d+)(\\s+)(?<currency>\\w{3}+)(.*)")
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(asAmount(v.get("amount")));
})
.section("fee", "currency").optional()
//
.match(".* Provision *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional()
//
.match(".* Eigene Spesen *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional()
//
.match(".* \\*Fremde Spesen *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("tax", "currency").optional() //
.match("(.*)Einbeh. Steuer(.*):(\\s*)(?<tax>[\\d.]+,\\d+) (?<currency>\\w{3}+)") //
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.TAX,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("tax"))))))
.wrap(t -> new BuySellEntryItem(t)));
}
@SuppressWarnings("nls")
private void addRemoveTransaction()
{
DocumentType type = new DocumentType("Bestandsausbuchung");
this.addDocumentTyp(type);
Block block = new Block("Bestandsausbuchung(.*)");
type.addBlock(block);
block.set(new Transaction<BuySellEntry>().subject(() -> {
BuySellEntry entry = new BuySellEntry();
entry.setType(PortfolioTransaction.Type.SELL);
return entry;
})
.section("date").match("Fälligkeitstag(\\s*):(\\s+)(?<date>\\d+.\\d+.\\d{4})(.*)") //
.assign((t, v) -> t.setDate(asDate(v.get("date"))))
.section("isin", "name")
.match("Bestandsausbuchung *(?<name>[^(]*) \\((?<isin>[^/]*)\\)") //
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("shares", "notation")
//Stk./Nominale**: 2.000,000000 Stk Einbeh. Steuer*: 0,00 EUR
.match("^Stk\\.\\/Nominale(.*):(\\s+)(?<shares>[\\.\\d]+(,\\d*)?)(\\s*)(?<notation>\\w{3}+)(.*)[Einbeh]+(.*)") //
.assign((t, v) -> {
String notation = v.get("notation");
if (notation != null && !notation.equalsIgnoreCase("Stk"))
{
// Prozent-Notierung, Workaround..
t.setShares((asShares(v.get("shares")) / 100));
}
else
{
t.setShares(asShares(v.get("shares")));
}
})
.section("amount", "currency").optional()
.match("(.*)Geldgegenwert\\*\\*(.*)(\\s*):(\\s*)(?<amount>[\\d.]+,\\d+)(\\s+)(?<currency>\\w{3}+)(.*)")
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(asAmount(v.get("amount")));
})
.section().optional()
.match("(.*)Bestand haben wir wertlos ausgebucht(.*)")
.assign((t, v) -> {
t.setCurrencyCode(t.getAccountTransaction().getSecurity().getCurrencyCode());
t.setAmount(0L);
t.getPortfolioTransaction().setType(PortfolioTransaction.Type.TRANSFER_OUT);
t.setType(PortfolioTransaction.Type.TRANSFER_OUT);
})
.section("fee", "currency").optional()
//
.match(".* Provision *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional()
//
.match(".* Eigene Spesen *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional()
//
.match(".* \\*Fremde Spesen *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("tax", "currency").optional() //
.match("(.*)Einbeh. Steuer(.*):(\\s*)(?<tax>[\\d.]+,\\d+) (?<currency>\\w{3}+)") //
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.TAX,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("tax"))))))
.wrap(t -> new BuySellEntryItem(t)));
}
//since ~2015
@SuppressWarnings("nls")
private void addRemoveNewFormatTransaction()
{
DocumentType type = new DocumentType("Gutschrifts- / Belastungsanzeige");
this.addDocumentTyp(type);
Block block = new Block("Kundennummer(.*)");
type.addBlock(block);
block.set(new Transaction<BuySellEntry>().subject(() -> {
BuySellEntry entry = new BuySellEntry();
entry.setType(PortfolioTransaction.Type.SELL);
return entry;
})
//WKN ISIN Wertpapierbezeichnung Anzahl
//SG0WRD DE000SG0WRD3 SG EFF. TURBOL ZS 83,00
//Sehr geehrter
.section("wkn", "isin", "name", "shares")
//.match("(?s)WKN(\\s+)ISIN(\\s+)Wertpapierbezeichnung(\\s+)Anzahl(.{1})(?<wkn>\\w{6}+)(\\s+)(?<isin>\\w{12}+)(\\s+)(?<name>.*?)(\\s+)(?<shares>[\\.\\d]+(,\\d*)?)(.*)(Sehr geehrte.*)") //
.match("(?s)(?<wkn>\\w{6}+)(\\s+)(?<isin>\\w{12}+)(\\s+)(?<name>.*?)(\\s+)(?<shares>[\\.\\d]+(,\\d*)?)(.*)") //
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
t.setShares(asShares(v.get("shares")));
})
.section("date").match("Fälligkeitstag(\\s*):(\\s+)(?<date>\\d+.\\d+.\\d{4})(.*)") //
.assign((t, v) -> t.setDate(asDate(v.get("date"))))
.section("amount", "currency").optional()
.match("(.*)Geldgegenwert(\\*{1,3})(.*)(\\s*):(\\s*)(?<amount>[\\d.]+,\\d+)(\\s+)(?<currency>\\w{3}+)(.*)")
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(asAmount(v.get("amount")));
})
.section().optional()
.match("(.*)Bestand haben wir wertlos ausgebucht(.*)")
.assign((t, v) -> {
t.setCurrencyCode(t.getAccountTransaction().getSecurity().getCurrencyCode());
t.setAmount(0L);
t.getPortfolioTransaction().setType(PortfolioTransaction.Type.TRANSFER_OUT);
t.setType(PortfolioTransaction.Type.TRANSFER_OUT);
})
.section("fee", "currency").optional()
//
.match(".* Provision *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional()
//
.match(".* Eigene Spesen *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("fee", "currency").optional()
//
.match(".* \\*Fremde Spesen *(?<currency>\\w{3}+) *(?<fee>[\\d.-]+,\\d+)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee"))))))
.section("tax", "currency").optional() //
.match("(.*)Einbeh. Steuer(.*):(\\s*)(?<tax>[\\d.]+,\\d+) (?<currency>\\w{3}+)") //
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.TAX,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("tax"))))))
.wrap(t -> new BuySellEntryItem(t)));
}
@SuppressWarnings("nls")
private void addOverdraftinterestTransaction()
{
final DocumentType type = new DocumentType("Kontoauszug Nr:", (context, lines) -> {
Pattern pYear = Pattern.compile("Kontoauszug Nr:[ ]*\\d+/(\\d+).*");
Pattern pCurrency = Pattern.compile("Kontow.hrung:[ ]+(\\w{3}+)");
// read the current context here
for (String line : lines)
{
Matcher m = pYear.matcher(line);
if (m.matches())
{
context.put("year", m.group(1));
}
m = pCurrency.matcher(line);
if (m.matches())
{
context.put("currency", m.group(1));
}
}
});
this.addDocumentTyp(type);
Block block = new Block("\\d+\\.\\d+\\.[ ]+\\d+\\.\\d+\\.[ ]+Zinsabschluss[ ]+(.*)");
type.addBlock(block);
block.set(new Transaction<AccountTransaction>()
.subject(() -> {
AccountTransaction t = new AccountTransaction();
t.setType(AccountTransaction.Type.INTEREST_CHARGE);
return t;
})
.section("valuta", "amount")
.match("\\d+.\\d+.[ ]+(?<valuta>\\d+.\\d+.)[ ]+Zinsabschluss[ ]+(\\d+.\\d+.\\d{4})(\\s+)-(\\s+)(\\d+.\\d+.\\d{4})(\\s+)(?<amount>[\\d.-]+,\\d+[+-])")
.assign((t, v) -> {
Map<String, String> context = type.getCurrentContext();
String date = v.get("valuta");
if (date != null)
{
// create a long date from the year in the
// context
t.setDate(asDate(date + context.get("year")));
}
t.setNote(v.get("text"));
t.setAmount(asAmount(v.get("amount")));
t.setCurrencyCode(asCurrencyCode(context.get("currency")));
}).wrap(t -> {
if (t.getAmount() != 0) { return new TransactionItem(t); }
return null;
}));
}
@SuppressWarnings("nls")
private void addTaxoptimisationTransaction()
{
final DocumentType type = new DocumentType("Kontoauszug Nr:", (context, lines) -> {
Pattern pYear = Pattern.compile("Kontoauszug Nr:[ ]*\\d+/(\\d+).*");
Pattern pCurrency = Pattern.compile("Kontow.hrung:[ ]+(\\w{3}+)");
// read the current context here
for (String line : lines)
{
Matcher m = pYear.matcher(line);
if (m.matches())
{
context.put("year", m.group(1));
}
m = pCurrency.matcher(line);
if (m.matches())
{
context.put("currency", m.group(1));
}
}
});
this.addDocumentTyp(type);
Block block = new Block("\\d+\\.\\d+\\.[ ]+\\d+\\.\\d+\\.[ ]+Steuertopfoptimierung[ ]+(.*)");
type.addBlock(block);
block.set(new Transaction<AccountTransaction>()
.subject(() -> {
AccountTransaction t = new AccountTransaction();
t.setType(AccountTransaction.Type.TAX_REFUND);
return t;
})
.section("valuta", "amount", "sign")
.match("\\d+.\\d+.[ ]+(?<valuta>\\d+.\\d+.)[ ]+Steuertopfoptimierung[ ]+(\\d{4})(\\s+)(?<amount>[\\d.-]+,\\d+)(?<sign>[+-])")
.assign((t, v) -> {
Map<String, String> context = type.getCurrentContext();
String date = v.get("valuta");
if (date != null)
{
// create a long date from the year in the
// context
t.setDate(asDate(date + context.get("year")));
}
t.setNote(v.get("text"));
t.setAmount(asAmount(v.get("amount")));
t.setCurrencyCode(asCurrencyCode(context.get("currency")));
String sign = v.get("sign");
if ("-".equals(sign))
{
// change type for payed Taxes
t.setType(AccountTransaction.Type.TAXES);
}
}).wrap(t -> {
if (t.getAmount() != 0) { return new TransactionItem(t); }
return null;
}));
}
@SuppressWarnings("nls")
private void addTaxReturnBlock(DocumentType type)
{
// optional: Steuererstattung
Block block = new Block("Nr.(\\d*)/(\\d*) Verkauf.*");
type.addBlock(block);
block.set(new Transaction<AccountTransaction>()
.subject(() -> {
AccountTransaction entry = new AccountTransaction();
entry.setType(AccountTransaction.Type.TAX_REFUND);
return entry;
})
.section("taxreturn")
.optional()
.match(".* \\*\\*Einbeh. Steuer *: *(?<taxreturn>-[\\d.]+,\\d+) (?<currency>\\w{3}+)")
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(asAmount(v.get("taxreturn")));
})
.section("wkn", "isin", "name")
.match("Nr.(\\d*)/(\\d*) Verkauf *(?<name>[^(]*) \\((?<isin>[^/]*)/(?<wkn>[^)]*)\\)")
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("shares", "date")
.match("^davon ausgef\\. *: (?<shares>[.\\d]+,\\d*) St\\. *Schlusstag *: *(?<date>\\d+.\\d+.\\d{4}+), \\d+:\\d+ Uhr")
.assign((t, v) -> {
t.setShares(asShares(v.get("shares")));
t.setDate(asDate(v.get("date")));
})
.wrap(t -> {
if (t.getCurrencyCode() != null && t.getAmount() != 0) { return new TransactionItem(t); }
return null;
}));
}
@Override
public String getLabel()
{
return "flatex"; //$NON-NLS-1$
}
}