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;
@SuppressWarnings("nls")
public class OnvistaPDFExtractor extends AbstractPDFExtractor
{
public OnvistaPDFExtractor(Client client) throws IOException
{
super(client);
addBankIdentifier(""); //$NON-NLS-1$
addBuyTransaction();
addSellTransaction();
addChangeTransaction();
addPayingTransaction();
addDividendTransaction();
// addBackOfProfitsTransaction();
addTransferInTransaction();
addCapitalReductionTransaction();
addCapitalIncreaseTransaction();
addAddDididendRightsTransaction();
addRemoveDididendRightsTransaction();
addExchangeTransaction();
addCompensationTransaction();
addDepositTransaction();
addAccountStatementTransaction();
}
private void addBuyTransaction()
{
DocumentType type = new DocumentType("Wir haben für Sie gekauft");
this.addDocumentTyp(type);
Block block = new Block("Wir haben für Sie gekauft(.*)");
type.addBlock(block);
Transaction<BuySellEntry> pdfTransaction = new Transaction<BuySellEntry>();
pdfTransaction.subject(() -> {
BuySellEntry entry = new BuySellEntry();
entry.setType(PortfolioTransaction.Type.BUY);
return entry;
});
block.set(pdfTransaction);
pdfTransaction.section("name", "isin") //
.find("Gattungsbezeichnung ISIN") //
.match("(?<name>.*) (?<isin>[^ ]\\S*)$") //
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("notation", "shares") //
.find("Nominal Kurs") //
.match("(?<notation>^\\w{3}+) (?<shares>\\d{1,3}(\\.\\d{3})*(,\\d{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("date", "amount", "currency") //
.find("Wert(\\s+)Konto-Nr. Betrag zu Ihren Lasten(\\s*)$")
// 14.01.2015 172306238 EUR 59,55
// Wert Konto-Nr. Betrag zu Ihren Lasten
// 01.06.2011 172306238 EUR 6,40
.match("(?<date>\\d+.\\d+.\\d{4}+) (\\d{6,12}) (?<currency>\\w{3}+) (?<amount>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)$")
.assign((t, v) -> {
t.setDate(asDate(v.get("date")));
t.setAmount(asAmount(v.get("amount")));
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
})
.wrap(t -> new BuySellEntryItem(t));
addFeesSectionsTransaction(pdfTransaction);
}
private void addSellTransaction()
{
DocumentType type = new DocumentType("Wir haben für Sie verkauft");
this.addDocumentTyp(type);
Block block = new Block("Wir haben für Sie verkauft(.*)");
type.addBlock(block);
Transaction<BuySellEntry> pdfTransaction = new Transaction<BuySellEntry>();
pdfTransaction.subject(() -> {
BuySellEntry entry = new BuySellEntry();
entry.setType(PortfolioTransaction.Type.SELL);
return entry;
});
block.set(pdfTransaction);
pdfTransaction.section("name", "isin") //
.find("Gattungsbezeichnung ISIN") //
.match("(?<name>.*) (?<isin>[^ ]\\S*)$") //
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
// Merken für evtl. Steuerrückerstattung:
type.getCurrentContext().put("isin", v.get("isin"));
})
.section("notation", "shares").find("Nominal Kurs")
.match("(?<notation>^\\w{3}+) (?<shares>\\d{1,3}(\\.\\d{3})*(,\\d{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("date", "amount", "currency")
.find("Wert(\\s+)Konto-Nr. Betrag zu Ihren Gunsten(\\s*)$")
// 12.04.2011 172306238 EUR 21,45
.match("(?<date>\\d+.\\d+.\\d{4}+) (\\d{6,12}) (?<currency>\\w{3}+) (?<amount>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)")
.assign((t, v) -> {
t.setDate(asDate(v.get("date")));
t.setAmount(asAmount(v.get("amount")));
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
})
.wrap(t -> new BuySellEntryItem(t));
addTaxesSectionsTransaction(pdfTransaction);
addFeesSectionsTransaction(pdfTransaction);
addTaxReturnBlock(type);
}
private void addChangeTransaction()
{
DocumentType type = new DocumentType("Bestätigung");
this.addDocumentTyp(type);
Block block = new Block("Bestätigung(.*)");
type.addBlock(block);
Transaction<BuySellEntry> pdfTransaction = new Transaction<BuySellEntry>();
pdfTransaction.subject(() -> {
BuySellEntry entry = new BuySellEntry();
entry.setType(PortfolioTransaction.Type.BUY);
return entry;
});
block.set(pdfTransaction);
pdfTransaction.section("name", "isin") //
.find("Gattungsbezeichnung ISIN") //
.match("(?<name>.*) (?<isin>[^ ]\\S*)$") //
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("notation", "shares") //
.find("Nominal Kurs")
.match("(?<notation>^\\w{3}+) (?<shares>\\d{1,3}(\\.\\d{3})*(,\\d{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("date", "amount", "currency") //
.find("Wert(\\s+)Konto-Nr. Betrag zu Ihren Lasten(\\s*)$")
// 14.01.2015 172306238 EUR 59,55
// Wert Konto-Nr. Betrag zu Ihren Lasten
// 01.06.2011 172306238 EUR 6,40
.match("(?<date>\\d+.\\d+.\\d{4}+) (\\d{6,12}) (?<currency>\\w{3}+) (?<amount>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)$")
.assign((t, v) -> {
t.setDate(asDate(v.get("date")));
t.setAmount(asAmount(v.get("amount")));
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
}).wrap(t -> new BuySellEntryItem(t));
addFeesSectionsTransaction(pdfTransaction);
}
private void addPayingTransaction()
{
DocumentType type = new DocumentType("Gutschriftsanzeige");
this.addDocumentTyp(type);
Block block = new Block("Gutschriftsanzeige(.*)");
type.addBlock(block);
Transaction<BuySellEntry> pdfTransaction = new Transaction<BuySellEntry>();
pdfTransaction.subject(() -> {
BuySellEntry entry = new BuySellEntry();
entry.setType(PortfolioTransaction.Type.SELL);
return entry;
});
block.set(pdfTransaction);
pdfTransaction.section("name", "isin") //
.find("Gattungsbezeichnung (.*) ISIN") //
.match("(?<name>.*) (.*) (?<isin>[^ ]\\S*)$") //
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("notation", "shares") //
.find("Nominal Einlösung(.*)$") //
.match("(?<notation>^\\w{3}+) (?<shares>\\d{1,3}(\\.\\d{3})*(,\\d{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("date", "amount", "currency") //
.find("Wert(\\s+)Konto-Nr. Betrag zu Ihren Gunsten$")
// 17.11.2014 172306238 EUR 51,85
.match("(?<date>\\d+.\\d+.\\d{4}+) (\\d{6,12}) (?<currency>\\w{3}+) (?<amount>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)")
.assign((t, v) -> {
t.setDate(asDate(v.get("date")));
t.setAmount(asAmount(v.get("amount")));
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
})
.wrap(t -> new BuySellEntryItem(t));
addTaxesSectionsTransaction(pdfTransaction);
}
private void addDividendTransaction()
{
DocumentType type = new DocumentType("Erträgnisgutschrift");
this.addDocumentTyp(type);
// Erträgnisgutschrift allein ist nicht gut hier, da es schon in der
// Kopfzeile steht..
Block block = new Block("Dividendengutschrift.*|Kupongutschrift.*|Erträgnisgutschrift.*(\\d+.\\d+.\\d{4})");
type.addBlock(block);
Transaction<AccountTransaction> pdfTransaction = new Transaction<>();
pdfTransaction.subject(() -> {
AccountTransaction transaction = new AccountTransaction();
transaction.setType(AccountTransaction.Type.DIVIDENDS);
return transaction;
});
block.set(pdfTransaction);
pdfTransaction
.section("name", "isin") //
.find("Gattungsbezeichnung(.*) ISIN")
// Commerzbank AG Inhaber-Aktien o.N. DE000CBK1001
// 5,5% TUI AG Wandelanl.v.2009(2014) 17.11.2014
// 17.11.2010 DE000TUAG117
.match("(?<name>.*?) (\\d+.\\d+.\\d{4} ){0,2}(?<isin>[^ ]\\S*)$") //
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("notation", "shares", "date", "amount", "currency")
// .find("Nominal (Ex-Tag )?Zahltag (.*etrag pro
// .*)?(Zinssatz.*)?")
// STK 25,000 17.05.2013 17.05.2013 EUR 0,700000
// Leistungen aus dem steuerlichen Einlagenkonto (§27
// KStG) EUR 17,50
.match("(?<notation>^\\w{3}+) (?<shares>[\\d.]+(,\\d*)?) (\\d+.\\d+.\\d{4}+) (?<date>\\d+.\\d+.\\d{4}+)?(.*)")
.match("(?<date>\\d+.\\d+.\\d{4}+)?(\\d{6,12})?(.{7,58} )?(?<currency>\\w{3}+) (?<amount>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)")
.assign((t, v) -> {
String notation = v.get("notation");
if (notation != null && !"STK".equalsIgnoreCase(notation))
{
// Prozent-Notierung, Workaround..
t.setShares(asShares(v.get("shares")) / 100);
}
else
{
t.setShares(asShares(v.get("shares")));
}
t.setDate(asDate(v.get("date")));
t.setAmount(asAmount(v.get("amount")));
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
})
.wrap(TransactionItem::new);
addTaxesSectionsTransaction(pdfTransaction);
// optional: Reinvestierung in:
block = new Block("Reinvestierung.*");
type.addBlock(block);
block.set(new Transaction<BuySellEntry>()
.subject(() -> {
BuySellEntry entry = new BuySellEntry();
entry.setType(PortfolioTransaction.Type.BUY);
return entry;
})
.section("date")
.match("(^\\w{3}+) (\\d{1,3}(\\.\\d{3})*(,\\d{3})?) (\\d+.\\d+.\\d{4}+) (?<date>\\d+.\\d+.\\d{4}+)?(.*)")
.assign((t, v) -> t.setDate(asDate(v.get("date"))))
.section("name", "isin") //
.find("Die Dividende wurde wie folgt in neue Aktien reinvestiert:")
.find("Gattungsbezeichnung ISIN") //
.match("(?<name>.*) (?<isin>[^ ]\\S*)$") //
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("notation", "shares", "amount", "currency") //
.find("Nominal Reinvestierungspreis")
// STK 25,000 EUR 0,700000
.match("(?<notation>^\\w{3}+) (?<shares>\\d{1,3}(\\.\\d{3})*(,\\d{3})?) (?<currency>\\w{3}+) (?<amount>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)(.*)")
.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")));
}
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount((asAmount(v.get("amount")) * asAmount(v.get("shares")) / 100));
})
.wrap(t -> new BuySellEntryItem(t)));
}
private void addTransferInTransaction()
{
DocumentType type = new DocumentType("Wir erhielten zu Gunsten Ihres Depots");
this.addDocumentTyp(type);
Block block = new Block("Wir erhielten zu Gunsten Ihres Depots(.*)");
type.addBlock(block);
Transaction<BuySellEntry> pdfTransaction = new Transaction<BuySellEntry>();
pdfTransaction.subject(() -> {
BuySellEntry entry = new BuySellEntry();
entry.setType(PortfolioTransaction.Type.TRANSFER_IN);
return entry;
});
block.set(pdfTransaction);
pdfTransaction.section("name", "isin") //
.find("Gattungsbezeichnung ISIN") //
.match("(?<name>.*) (?<isin>[^ ]\\S*)$") //
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("notation", "shares", "date") //
.find("Nominal Schlusstag Wert")
// STK 28,000 02.12.2011 02.12.2011
.match("(?<notation>^\\w{3}+) (?<shares>\\d{1,3}(\\.\\d{3})*(,\\d{3})?) (\\d+.\\d+.\\d{4}+) (?<date>\\d+.\\d+.\\d{4}+)(.*)")
.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")));
}
t.setDate(asDate(v.get("date")));
t.setCurrencyCode(asCurrencyCode(
t.getPortfolioTransaction().getSecurity().getCurrencyCode()));
})
.wrap(t -> new BuySellEntryItem(t));
addFeesSectionsTransaction(pdfTransaction);
}
private void addCapitalReductionTransaction()
{
DocumentType type = new DocumentType("Kapitalherabsetzung");
this.addDocumentTyp(type);
Block block = new Block("(Aus|Ein)buchung:(.*)");
type.addBlock(block);
Transaction<PortfolioTransaction> pdfTransaction = new Transaction<PortfolioTransaction>();
pdfTransaction.subject(() -> {
PortfolioTransaction entry = new PortfolioTransaction();
entry.setType(PortfolioTransaction.Type.DELIVERY_INBOUND);
return entry;
});
block.set(pdfTransaction);
pdfTransaction.section("date")
.optional()
// STK 55,000 24.04.2013
.match("(^\\w{3}+) (\\d{1,3}(\\.\\d{3})*(,\\d{3})?) (?<date>\\d+.\\d+.\\d{4}+)?(.*)")
.assign((t, v) -> {
t.setDate(asDate(v.get("date")));
type.getCurrentContext().put("date", v.get("date"));
})
.section("name", "isin")
.find("Gattungsbezeichnung ISIN")
.match("(?<name>.*) (?<isin>[^ ]\\S*)$")
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("transactiontype")
.match("^(?<transactiontype>.*buchung:)(.*)")
.assign((t, v) -> {
String transactiontype = v.get("transactiontype");
if ("Einbuchung:".equalsIgnoreCase(transactiontype))
{
t.setType(PortfolioTransaction.Type.DELIVERY_INBOUND);
}
else if ("Ausbuchung:".equalsIgnoreCase(transactiontype))
{
t.setType(PortfolioTransaction.Type.DELIVERY_OUTBOUND);
}
else
{
; // TODO: evtl. Warnung/Hinweis ausgeben?
}
})
// Nominal Ex-Tag
// STK 55,000 24.04.2013
.section("notation", "shares")
.find("Nominal(.*)")
.match("(?<notation>^\\w{3}+) (?<shares>\\d{1,3}(\\.\\d{3})*(,\\d{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")));
}
t.setCurrencyCode(asCurrencyCode(t.getSecurity().getCurrencyCode()));
if (t.getDate() == null)
{
t.setDate(asDate(type.getCurrentContext().get("date")));
}
})
.wrap(t -> new TransactionItem(t));
}
private void addCapitalIncreaseTransaction()
{
DocumentType type = new DocumentType("Kapitalerhöhung");
this.addDocumentTyp(type);
Block block = new Block("Kapitalerhöhung(.*)");
type.addBlock(block);
Transaction<PortfolioTransaction> pdfTransaction = new Transaction<PortfolioTransaction>();
pdfTransaction.subject(() -> {
PortfolioTransaction entry = new PortfolioTransaction();
entry.setType(PortfolioTransaction.Type.DELIVERY_INBOUND);
return entry;
});
block.set(pdfTransaction);
pdfTransaction.section("date")
// Frankfurt am Main, 06.04.2011
.match("(.*), (?<date>\\d+.\\d+.\\d{4}+)")
.assign((t, v) -> {
t.setDate(asDate(v.get("date")));
})
.section("name", "isin")
.find("Einbuchung:(\\s*)")
.find("Gattungsbezeichnung ISIN")
.match("(?<name>.*) (?<isin>[^ ]\\S*)$").assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
// Nominal
// STK 55,000
.section("notation", "shares")
.find("Nominal(.*)")
.match("(?<notation>^\\w{3}+) (?<shares>\\d{1,3}(\\.\\d{3})*(,\\d{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")));
}
t.setCurrencyCode(asCurrencyCode(t.getSecurity().getCurrencyCode()));
})
.wrap(t -> new TransactionItem(t));
}
private void addAddDididendRightsTransaction()
{
DocumentType type = new DocumentType("Einbuchung von Rechten für die");
this.addDocumentTyp(type);
Block block = new Block("Einbuchung von Rechten für die(.*)");
type.addBlock(block);
Transaction<PortfolioTransaction> pdfTransaction = new Transaction<PortfolioTransaction>();
pdfTransaction.subject(() -> {
PortfolioTransaction entry = new PortfolioTransaction();
entry.setType(PortfolioTransaction.Type.DELIVERY_INBOUND);
return entry;
});
block.set(pdfTransaction);
pdfTransaction.section("date")
// Frankfurt am Main, 25.05.2016
.match("(.*), (?<date>\\d+.\\d+.\\d{4}+)")
.assign((t, v) -> {
t.setDate(asDate(v.get("date")));
})
.section("name", "isin")
.find("Einbuchung:(\\s*)")
.find("Gattungsbezeichnung ISIN")
.match("(?<name>.*) (?<isin>[^ ]\\S*)$")
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("notation", "shares")
.find("Nominal(.*)")
.match("(?<notation>^\\w{3}+) (?<shares>\\d{1,3}(\\.\\d{3})*(,\\d{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")));
}
t.setCurrencyCode(asCurrencyCode(t.getSecurity().getCurrencyCode()));
})
.wrap(t -> new TransactionItem(t));
}
private void addRemoveDididendRightsTransaction()
{
DocumentType type = new DocumentType("Wertlose Ausbuchung");
this.addDocumentTyp(type);
Block block = new Block("Wertlose Ausbuchung(.*)");
type.addBlock(block);
Transaction<PortfolioTransaction> pdfTransaction = new Transaction<PortfolioTransaction>();
pdfTransaction.subject(() -> {
PortfolioTransaction entry = new PortfolioTransaction();
entry.setType(PortfolioTransaction.Type.DELIVERY_OUTBOUND);
return entry;
});
block.set(pdfTransaction);
pdfTransaction
.section("name", "isin")
.find("Ausbuchung:(\\s*)")
.find("Gattungsbezeichnung ISIN")
.match("(?<name>.*) (?<isin>[^ ]\\S*)$")
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("notation", "shares", "date")
.find("Nominal Ex-Tag")
// STK 25,000 21.06.2016
.match("(?<notation>^\\w{3}+) (?<shares>\\d{1,3}(\\.\\d{3})*(,\\d{3})?) (?<date>\\d+.\\d+.\\d{4}+)(.*)")
.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")));
}
t.setCurrencyCode(asCurrencyCode(t.getSecurity().getCurrencyCode()));
t.setDate(asDate(v.get("date")));
})
.wrap(t -> new TransactionItem(t));
}
private void addExchangeTransaction()
{
DocumentType type = new DocumentType("Umtausch");
this.addDocumentTyp(type);
Block block = new Block("(Aus|Ein)buchung:(.*)");
type.addBlock(block);
Transaction<PortfolioTransaction> pdfTransaction = new Transaction<PortfolioTransaction>();
pdfTransaction.subject(() -> {
PortfolioTransaction entry = new PortfolioTransaction();
entry.setType(PortfolioTransaction.Type.DELIVERY_OUTBOUND);
return entry;
});
block.set(pdfTransaction);
pdfTransaction.section("name", "isin")
.find("Gattungsbezeichnung ISIN")
.match("(?<name>.*) (?<isin>[^ ]\\S*)$")
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
// Merken für evtl. Steuerrückerstattung:
type.getCurrentContext().put("isin", v.get("isin"));
})
.section("transactiontype")
.match("^(?<transactiontype>.*buchung:)(.*)")
.assign((t, v) -> {
String transactiontype = v.get("transactiontype");
if ("Einbuchung:".equalsIgnoreCase(transactiontype))
{
t.setType(PortfolioTransaction.Type.DELIVERY_INBOUND);
}
else if ("Ausbuchung:".equalsIgnoreCase(transactiontype))
{
t.setType(PortfolioTransaction.Type.DELIVERY_OUTBOUND);
}
else
{
; // TODO: evtl. Warnung/Hinweis ausgeben?
}
})
.section("date")
.find("(.*)(Schlusstag|Ex-Tag|Wert Konto-Nr.*)")
.match("(.*)(^|\\s+)(?<date>\\d+.\\d+.\\d{4}+)")
.assign((t, v) -> {
t.setDate(asDate(v.get("date")));
})
.section("currency", "amount")
.optional()
// Wert Betrag zu Ihren Gunsten
// EUR 0,00
// oder
// Wert Konto-Nr. Betrag zu Ihren Lasten
// 23.11.2015 172306238 EUR 12,86
.find("(.*)(Schlusstag|Ex-Tag|Wert Konto-Nr.*|Wert Betrag zu Ihren.*)")
.match("(^|\\s+)(\\d+\\.\\d+\\.\\d{4}+)?(\\s)?(\\d+)?(\\s)?(?<currency>\\w{3}+) (?<amount>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)")
.assign((t, v) -> {
t.setAmount(asAmount(v.get("amount")));
})
.section("notation", "shares")
.find("Nominal(.*)")
.match("(?<notation>^\\w{3}+) (?<shares>\\d{1,3}(\\.\\d{3})*(,\\d{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")));
}
t.setCurrencyCode(asCurrencyCode(t.getSecurity()
.getCurrencyCode()));
})
.wrap(t -> {
return new TransactionItem(t);
});
addTaxesSectionsTransaction(pdfTransaction);
addTaxReturnBlock(type);
addTaxBlock(type);
}
private void addCompensationTransaction()
{
DocumentType type = new DocumentType("Abfindung");
this.addDocumentTyp(type);
Block block = new Block("Ausbuchung(.*)");
type.addBlock(block);
Transaction<BuySellEntry> pdfTransaction = new Transaction<BuySellEntry>();
pdfTransaction.subject(() -> {
BuySellEntry entry = new BuySellEntry();
entry.setType(PortfolioTransaction.Type.SELL);
return entry;
});
block.set(pdfTransaction);
pdfTransaction.section("name", "isin")
.find("Gattungsbezeichnung ISIN")
.match("(?<name>.*) (?<isin>[^ ]\\S*)$")
.assign((t, v) -> {
t.setSecurity(getOrCreateSecurity(v));
})
.section("transactiontype")
.match("^(?<transactiontype>.*buchung:)(.*)")
.assign((t, v) -> {
String transactiontype = v.get("transactiontype");
if ("Einbuchung:".equalsIgnoreCase(transactiontype))
{
t.getAccountTransaction().setType(AccountTransaction.Type.BUY);
t.getPortfolioTransaction().setType(PortfolioTransaction.Type.DELIVERY_INBOUND);
}
else if ("Ausbuchung:".equalsIgnoreCase(transactiontype))
{
t.getAccountTransaction().setType(AccountTransaction.Type.SELL);
t.getPortfolioTransaction().setType(PortfolioTransaction.Type.DELIVERY_OUTBOUND);
}
else
{
; // TODO: evtl. Warnung/Hinweis ausgeben?
}
})
.section("notation", "shares", "date")
.find("Nominal Ex-Tag")
// STK 25,000 11.06.2013
.match("(?<notation>^\\w{3}+) (?<shares>\\d{1,3}(\\.\\d{3})*(,\\d{3})?) (?<date>\\d+.\\d+.\\d{4}+)(.*)")
.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")));
}
t.setDate(asDate(v.get("date")));
t.setCurrencyCode(asCurrencyCode(t.getPortfolioTransaction().getSecurity()
.getCurrencyCode()));
})
.section("date", "amount", "currency")
.find("Wert(\\s+)Konto-Nr. Betrag zu Ihren Gunsten(\\s*)$")
// 11.06.2013 172306238 EUR 17,50
.match("(?<date>\\d+.\\d+.\\d{4}+) (\\d{6,12}) (?<currency>\\w{3}+) (?<amount>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)")
.assign((t, v) -> {
t.setDate(asDate(v.get("date")));
t.setAmount(asAmount(v.get("amount")));
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
})
.wrap(t -> new BuySellEntryItem(t));
addFeesSectionsTransaction(pdfTransaction);
}
private void addDepositTransaction()
{
final DocumentType type = new DocumentType("Depotauszug", (context, lines) -> {
Pattern pDate = Pattern.compile("Depotauszug per (\\d+.\\d+.\\d{4}+)?(.*)");
Pattern pCurrency = Pattern.compile("(.*)Bewertung in[ ]+(\\w{3}+)");
// read the current context here
for (String line : lines)
{
Matcher m = pDate.matcher(line);
if (m.matches())
{
context.put("date", m.group(1));
}
m = pCurrency.matcher(line);
if (m.matches())
{
context.put("currency", m.group(2));
}
}
});
this.addDocumentTyp(type);
Block block = new Block("(^\\w{3}+) (\\d{1,3}(\\.\\d{3})*(,\\d{3})?)(.*)");
type.addBlock(block);
Transaction<PortfolioTransaction> pdfTransaction = new Transaction<PortfolioTransaction>();
pdfTransaction.subject(() -> {
PortfolioTransaction entry = new PortfolioTransaction();
entry.setType(PortfolioTransaction.Type.DELIVERY_INBOUND);
return entry;
});
block.set(pdfTransaction);
// Die WP-Bezeichnung muss hier leider über mehrere Zeilen hinweg
// zusammengesucht werden, da im Depotauszug-PDF-Extrakt leider
// (zumindest teilweise) Zeilenumbrüche innerhalb des Namens sind... (s.
// Beispiel-Datei: OnvistaDepotauszug.txt)
pdfTransaction
.section("notation", "shares", "nameP1").optional()
// STK 4,000 Porsche Automobil
.match("(?<notation>^\\w{3}+) (?<shares>\\d{1,3}(\\.\\d{3})*(,\\d{3})?) (?<nameP1>.*)")
.assign((t, v) -> {
type.getCurrentContext().put("nameP1", v.get("nameP1"));
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("nameP3").optional()
.find("(^\\w{3}+) (\\d{1,3}(\\.\\d{3})*(,\\d{3})?) (.*)")
// Inhaber-Vorzugsakti
.match("^(?<nameP3>^.*?)(\\s*)")
.assign((t, v) -> {
type.getCurrentContext().put("nameP3", v.get("nameP3"));
})
.section("nameP2", "isin").optional()
// Holding SE DE000PAH0038 Girosammelverwahrung 59,3400
.match("(?<nameP2>.* )(?<isin>\\w{12}+) (.*)")
.assign((t, v) -> {
type.getCurrentContext().put("nameP2", v.get("nameP2"));
type.getCurrentContext().put("isin", v.get("isin"));
})
//
.section("nameP4").optional()
.find("^(.*) (\\w{12}+) (.*)")
// en o.St.o.N
.match("^(?<nameP4>^.*\\.*)$")
.assign((t, v) -> {
type.getCurrentContext().put("nameP4", v.get("nameP4"));
})
.section("combine")
.match("(?<combine>.*)")
.assign((t, v) -> {
String name = type.getCurrentContext().get("nameP1") +
type.getCurrentContext().get("nameP2") +
type.getCurrentContext().get("nameP3") +
type.getCurrentContext().get("nameP4");
v.put("isin", type.getCurrentContext().get("isin"));
if (name.indexOf(v.get("isin")) > -1)
{
name = name.substring(0, name.indexOf(v.get("isin")));
}
if (name.indexOf("STK ") > -1)
{
name = name.substring(0, name.indexOf("STK "));
}
// WP-Bezeichnung nachbearbeiten, kann doppelte
// Leerzeichen enthalten...
name = name.replaceAll("\\s+", " ");
// oder auch überflüssige Nullen (00)...
name = name.replaceAll("0+%", "%");
// oder <Leerzeichen><Punkt> ( .)
name = name.replaceAll("\\s+\\.", ".");
v.put("name", name);
t.setSecurity(getOrCreateSecurity(v));
// System.out.println("WP: Stk-T: " + t.getShares()
// + " " + t.getSecurity().getIsin() + " "
// + name + "\n");
if (t.getDate() == null)
{
t.setDate(asDate(type.getCurrentContext().get("date")));
}
if (t.getCurrencyCode() == null)
{
t.setCurrencyCode(asCurrencyCode(type.getCurrentContext().get("currency")));
}
})
.wrap(t -> new TransactionItem(t));
}
private void addAccountStatementTransaction()
{
final DocumentType type = new DocumentType("KONTOAUSZUG Nr.", (context, lines) -> {
Pattern pYear = Pattern.compile("KONTOAUSZUG Nr. \\d+ per \\d+.\\d+.(\\d{4}+)?(.*)");
Pattern pCurrency = Pattern.compile("(.*)Customer Cash Account[ ]+(\\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(2));
}
}
});
this.addDocumentTyp(type);
// 31.10. 31.10. REF: 000017304356 37,66
Block block = new Block("^\\d+\\.\\d+\\.\\s+\\d+\\.\\d+\\.\\s+REF:\\s+\\d+\\s+[\\d.-]+,\\d+[+-]?(.*)");
type.addBlock(block);
Transaction<AccountTransaction> pdfTransaction = new Transaction<AccountTransaction>();
pdfTransaction.subject(() -> {
AccountTransaction entry = new AccountTransaction();
entry.setType(AccountTransaction.Type.DEPOSIT);
return entry;
});
block.set(pdfTransaction);
pdfTransaction.section("valuta", "amount", "sign")
.match("^\\d+\\.\\d+\\.\\s+(?<valuta>\\d+\\.\\d+\\.)\\s+REF:\\s+\\d+\\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.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);
}
})
// Feintuning Buchungstyp...
.section("postingtype")
.find("\\d+\\.\\d+\\.\\s+\\d+\\.\\d+\\. REF:(.*)")
.match("(?<postingtype>.*?)")
.assign((t, v) -> {
String postingtype = v.get("postingtype");
if (postingtype != null)
{
switch (postingtype)
{
case "Wertpapierkauf":
case "Umtausch/Bezug":
case "Sollbuchung HSBC":
t.setType(AccountTransaction.Type.BUY);
break;
case "Wertpapierverkauf":
case "Spitze Verkauf":
case "Habenbuchung HSBC":
case "Tilgung":
t.setType(AccountTransaction.Type.SELL);
break;
case "Zinsen/Dividenden":
t.setType(AccountTransaction.Type.DIVIDENDS);
break;
case "AbgSt. Optimierung":
t.setType(AccountTransaction.Type.TAX_REFUND);
break;
}
}
})
.wrap(t -> {
// Buchungen, die bereits durch den Import von
// WP-Abrechnungen abgedeckt sind (sein sollten),
// hier ausklammern, sonst hat man sie doppelt im
// Konto:
if (t.getType() != AccountTransaction.Type.DIVIDENDS
&& t.getType() != AccountTransaction.Type.BUY
&& t.getType() != AccountTransaction.Type.SELL
&& t.getType() != AccountTransaction.Type.TAX_REFUND)
{
return new TransactionItem(t);
}
return null;
});
}
private <T extends Transaction<?>> void addTaxesSectionsTransaction(T pdfTransaction)
{
pdfTransaction.section("tax", "withheld", "sign")
.optional()
.match("(?<withheld>\\w+|^)(\\s*)Kapitalertragsteuer(\\s*)(?<currency>\\w{3}+)(\\s+)(?<tax>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)(?<sign>-|\\s+|$)?")
.assign((t, v) -> {
if ("-".equalsIgnoreCase(v.get("sign"))
|| "einbehaltene".equalsIgnoreCase(v.get("withheld")))
{
if (t instanceof name.abuchen.portfolio.model.Transaction)
{
((name.abuchen.portfolio.model.Transaction) t)
.addUnit(new Unit(Unit.Type.TAX,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("tax")))));
}
else
{
((name.abuchen.portfolio.model.BuySellEntry) t).getPortfolioTransaction().addUnit(
new Unit(Unit.Type.TAX, Money.of(asCurrencyCode(v.get("currency")),
asAmount(v.get("tax")))));
}
}
})
.section("soli", "withheld", "sign")
.optional()
.match("(?<withheld>\\w+|^)(\\s*)Solidaritätszuschlag(\\s*)(?<currency>\\w{3}+)(\\s+)(?<soli>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)(?<sign>-|\\s+|$)?")
.assign((t, v) -> {
if ("-".equalsIgnoreCase(v.get("sign"))
|| "einbehaltener".equalsIgnoreCase(v.get("withheld")))
{
if (t instanceof name.abuchen.portfolio.model.Transaction)
{
((name.abuchen.portfolio.model.Transaction) t).addUnit(new Unit(
Unit.Type.TAX,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("soli")))));
}
else
{
((name.abuchen.portfolio.model.BuySellEntry) t).getPortfolioTransaction().addUnit(
new Unit(Unit.Type.TAX,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("soli")))));
}
}
})
.section("kirchenst", "withheld", "sign")
.optional()
.match("(?<withheld>\\w+|^)(\\s*)Kirchensteuer(\\s*)(?<currency>\\w{3}+)(\\s+)(?<kirchenst>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)(?<sign>-|\\s+|$)?")
.assign((t, v) -> {
if ("-".equalsIgnoreCase(v.get("sign"))
|| "einbehaltene".equalsIgnoreCase(v.get("withheld")))
{
if (t instanceof name.abuchen.portfolio.model.Transaction)
{
((name.abuchen.portfolio.model.Transaction) t).addUnit(new Unit(Unit.Type.TAX,
Money.of(asCurrencyCode(v.get("currency")),
asAmount(v.get("kirchenst")))));
}
else
{
((name.abuchen.portfolio.model.BuySellEntry) t).getPortfolioTransaction().addUnit(
new Unit(Unit.Type.TAX,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("kirchenst")))));
}
}
});
}
private void addFeesSectionsTransaction(Transaction<BuySellEntry> pdfTransaction)
{
pdfTransaction.section("brokerage").optional()
.match("(^.*)(Orderprovision) (\\w{3}+) (?<brokerage>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)(-)")
.assign((t, v) -> t.getPortfolioTransaction()
.addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")),
asAmount(v.get("brokerage"))))))
.section("stockfees").optional()
.match("(^.*) (B\\Drsengeb\\Dhr) (\\w{3}+) (?<stockfees>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)(-)")
.assign((t, v) -> t.getPortfolioTransaction()
.addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")),
asAmount(v.get("stockfees"))))))
.section("stockfees2").optional()
.match("(^.*) (Handelsplatzgeb\\Dhr) (\\w{3}+) (?<stockfees2>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)(-)")
.assign((t, v) -> t.getPortfolioTransaction()
.addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")),
asAmount(v.get("stockfees2"))))))
.section("agent").optional()
.match("(^.*)(Maklercourtage)(\\s+)(\\w{3}+) (?<agent>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)(-)")
.assign((t, v) -> t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE,
Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("agent"))))));
}
private void addTaxReturnBlock(DocumentType type)
{
// optional: Steuererstattung
Block block = new Block("Steuerausgleich nach § 43a Abs. 3 Satz 2 EStG:(.*)");
type.addBlock(block);
block.set(new Transaction<AccountTransaction>()
.subject(() -> {
AccountTransaction entry = new AccountTransaction();
entry.setType(AccountTransaction.Type.TAX_REFUND);
return entry;
})
.section("tax")
.optional()
.match("^Kapitalertragsteuer (?<currency>\\w{3}+) (?<tax>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)")
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(asAmount(v.get("tax")));
})
.section("soli")
.optional()
.match("^Solidaritätszuschlag (?<currency>\\w{3}+) (?<soli>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)")
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(t.getAmount() + asAmount(v.get("soli")));
})
.section("kirchenst")
.optional()
.match("^Kirchensteuer (?<currency>\\w{3}+) (?<kirchenst>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)")
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(t.getAmount() + asAmount(v.get("kirchenst")));
})
.section("date", "currency")
.find("Wert(\\s+)Konto-Nr.(\\s+)Abrechnungs-Nr.(\\s+)Betrag zu Ihren Gunsten(\\s*)$")
// Wert Konto-Nr. Abrechnungs-Nr. Betrag zu Ihren
// Gunsten
// 06.05.2013 172306238 56072633 EUR 3,05
.match("(^|\\s+)(?<date>\\d+\\.\\d+\\.\\d{4}+)(\\s)(\\d+)?(\\s)?(\\d+)?(\\s)(?<currency>\\w{3}+) (\\d{1,3}(\\.\\d{3})*(,\\d{2})?)")
.assign((t, v) -> {
t.setDate(asDate(v.get("date")));
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
v.put("isin", type.getCurrentContext().get("isin"));
t.setSecurity(getOrCreateSecurity(v));
})
.wrap(t -> new TransactionItem(t)));
}
private void addTaxBlock(DocumentType type)
{
// optional: Steuer dem Konto buchen
Block block = new Block("(Kapitalertragsteuer)(.*)-$");
type.addBlock(block);
block.set(new Transaction<AccountTransaction>()
.subject(() -> {
AccountTransaction entry = new AccountTransaction();
entry.setType(AccountTransaction.Type.TAXES);
return entry;
})
.section("tax")
.optional()
.match("^Kapitalertragsteuer (?<currency>\\w{3}+) (?<tax>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)(-)")
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(asAmount(v.get("tax")));
})
.section("soli")
.optional()
.match("^Solidaritätszuschlag (?<currency>\\w{3}+) (?<soli>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)(-)")
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(t.getAmount() + asAmount(v.get("soli")));
})
.section("kirchenst")
.optional()
.match("^Kirchensteuer (?<currency>\\w{3}+) (?<kirchenst>\\d{1,3}(\\.\\d{3})*(,\\d{2})?)(-)")
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(t.getAmount() + asAmount(v.get("kirchenst")));
})
.section("date", "currency")
.optional()
.find("Wert(\\s+)Konto-Nr.(\\s+)Betrag zu Ihren Lasten(\\s*)$")
// Wert Konto-Nr. Betrag zu Ihren Lasten
// 23.11.2015 172306238 EUR 12,86
.match("(^|\\s+)(?<date>\\d+\\.\\d+\\.\\d{4}+)(\\s)(\\d+)(\\s)(?<currency>\\w{3}+) (\\d{1,3}(\\.\\d{3})*(,\\d{2})?)")
.assign((t, v) -> {
t.setDate(asDate(v.get("date")));
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
v.put("isin", type.getCurrentContext().get("isin"));
t.setSecurity(getOrCreateSecurity(v));
})
.wrap(t -> new TransactionItem(t)));
}
@Override
public String getLabel()
{
return "Onvista"; //$NON-NLS-1$
}
}