package name.abuchen.portfolio.datatransfer.pdf; import java.io.IOException; import java.math.BigDecimal; import java.util.Map; 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 DABPDFExctractor extends AbstractPDFExtractor { public DABPDFExctractor(Client client) throws IOException { super(client); addBankIdentifier("DAB"); //$NON-NLS-1$ addBuyTransaction(); addSellTransaction(); addDividendTransaction(); } @SuppressWarnings("nls") private void addBuyTransaction() { DocumentType type = new DocumentType("Kauf"); this.addDocumentTyp(type); Block block = new Block("^Kauf .*$"); type.addBlock(block); block.set(new Transaction<BuySellEntry>() .subject(() -> { BuySellEntry entry = new BuySellEntry(); entry.setType(PortfolioTransaction.Type.BUY); return entry; }) .section("isin", "name", "currency") // .find("Gattungsbezeichnung ISIN") // .match("^(?<name>.*) (?<isin>[^ ]*)$") .match("STK [\\d.]+(,\\d+)? (?<currency>\\w{3}+) ([\\d.]+,\\d+)$") .assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))) .section("shares") // .find("Nominal Kurs") // .match("^STK (?<shares>[\\d.]+(,\\d+)?) (\\w{3}+) ([\\d.]+,\\d+)$") .assign((t, v) -> t.setShares(asShares(v.get("shares")))) .section("amount", "currency") // .optional() // .find("Wert Konto-Nr. Betrag zu Ihren Lasten") .match("^(\\d+.\\d+.\\d{4}+) ([0-9]*) (?<currency>\\w{3}+) (?<amount>[\\d.]+,\\d+)$") .assign((t, v) -> { t.setAmount(asAmount(v.get("amount"))); t.setCurrencyCode(asCurrencyCode(v.get("currency"))); }) .section("amount", "currency", "exchangeRate", "forex", "forexCurrency").optional() // .find(".* Ausmachender Betrag (?<forexCurrency>\\w{3}+) (?<forex>[\\d.]+,\\d+)-") .find("Wert Konto-Nr. Devisenkurs Betrag zu Ihren Lasten") .match("^(\\d+.\\d+.\\d{4}+) ([0-9]*) .../... (?<exchangeRate>[\\d.]+,\\d+) (?<currency>\\w{3}+) (?<amount>[\\d.]+,\\d+)$") .assign((t, v) -> { Money amount = Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("amount"))); t.setMonetaryAmount(amount); BigDecimal exchangeRate = BigDecimal.ONE.divide( // asExchangeRate(v.get("exchangeRate")), 10, BigDecimal.ROUND_HALF_DOWN); Money forex = Money.of(asCurrencyCode(v.get("forexCurrency")), asAmount(v.get("forex"))); Unit grossValue = new Unit(Unit.Type.GROSS_VALUE, amount, forex, exchangeRate); t.getPortfolioTransaction().addUnit(grossValue); }) .section("date") // .match("^Handelstag (?<date>\\d+.\\d+.\\d{4}+) .*$") .assign((t, v) -> t.setDate(asDate(v.get("date")))) .section("fees", "currency") // .optional().match("^.* Provision (?<currency>\\w{3}+) (?<fees>[\\d.]+,\\d+)-$") .assign((t, v) -> { String currency = asCurrencyCode(v.get("currency")); // FIXME forex fees must update gross value if (currency.equals(t.getAccountTransaction().getCurrencyCode())) { t.getPortfolioTransaction().addUnit( new Unit(Unit.Type.FEE, Money.of(currency, asAmount(v.get("fees"))))); } }) .wrap(t -> { if (t.getPortfolioTransaction().getAmount() == 0L) throw new IllegalArgumentException("No amount found"); return new BuySellEntryItem(t); })); } @SuppressWarnings("nls") private void addSellTransaction() { DocumentType type = new DocumentType("Verkauf"); this.addDocumentTyp(type); Block block = new Block("^Verkauf .*$"); type.addBlock(block); block.set(new Transaction<BuySellEntry>() .subject(() -> { BuySellEntry entry = new BuySellEntry(); entry.setType(PortfolioTransaction.Type.SELL); return entry; }) .section("isin", "name", "currency") // .find("Gattungsbezeichnung ISIN") // .match("^(?<name>.*) (?<isin>[^ ]*)$") .match("STK [\\d.]+(,\\d+)? (?<currency>\\w{3}+) ([\\d.]+,\\d+)$") .assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))) .section("shares") // .find("Nominal Kurs") // .match("^STK (?<shares>[\\d.]+(,\\d+)?) (\\w{3}+) ([\\d.]+,\\d+)$") .assign((t, v) -> t.setShares(asShares(v.get("shares")))) .section("amount", "currency").optional() // .find("Wert Konto-Nr. Betrag zu Ihren Gunsten") .match("^(\\d+.\\d+.\\d{4}+) ([0-9]*) (?<currency>\\w{3}+) (?<amount>[\\d.]+,\\d+)$") .assign((t, v) -> { t.setAmount(asAmount(v.get("amount"))); t.setCurrencyCode(asCurrencyCode(v.get("currency"))); }) .section("amount", "currency", "exchangeRate", "forex", "forexCurrency").optional() // .find(".* Ausmachender Betrag (?<forexCurrency>\\w{3}+) (?<forex>[\\d.]+,\\d+)") .find("Wert Konto-Nr. Devisenkurs Betrag zu Ihren Gunsten") .match("^(\\d+.\\d+.\\d{4}+) ([0-9]*) .../... (?<exchangeRate>[\\d.]+,\\d+) (?<currency>\\w{3}+) (?<amount>[\\d.]+,\\d+)$") .assign((t, v) -> { Money amount = Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("amount"))); t.setMonetaryAmount(amount); BigDecimal exchangeRate = BigDecimal.ONE.divide( // asExchangeRate(v.get("exchangeRate")), 10, BigDecimal.ROUND_HALF_DOWN); Money forex = Money.of(asCurrencyCode(v.get("forexCurrency")), asAmount(v.get("forex"))); Unit grossValue = new Unit(Unit.Type.GROSS_VALUE, amount, forex, exchangeRate); t.getPortfolioTransaction().addUnit(grossValue); }) .section("date") // .match("^Handelstag (?<date>\\d+.\\d+.\\d{4}+) .*$") .assign((t, v) -> t.setDate(asDate(v.get("date")))) .section("fees", "currency").optional() .match("^.*Provision (?<currency>\\w{3}+) (?<fees>[\\d.]+,\\d+)-$").assign((t, v) -> { String currency = asCurrencyCode(v.get("currency")); if (currency.equals(t.getAccountTransaction().getCurrencyCode())) { t.getPortfolioTransaction().addUnit( new Unit(Unit.Type.FEE, Money.of(currency, asAmount(v.get("fees"))))); } }) .section("tax", "currency", "label").optional() .match("^(?<label>.*)Kapitalertragsteuer (?<currency>\\w{3}+) (?<tax>[\\d.]+,\\d+)-?$") .assign(this::addSellTaxUnit) .section("tax", "currency", "label").optional() .match("^(?<label>.*)Solidaritätszuschlag (?<currency>\\w{3}+) (?<tax>[\\d.]+,\\d+)-?$") .assign(this::addSellTaxUnit) .section("tax", "currency", "label").optional() .match("^(?<label>.*)Kirchensteuer (?<currency>\\w{3}+) (?<tax>[\\d.]+,\\d+)-?$") // .assign(this::addSellTaxUnit) .wrap(t -> { if (t.getPortfolioTransaction().getAmount() == 0L) throw new IllegalArgumentException("No amount found"); return new BuySellEntryItem(t); })); } @SuppressWarnings("nls") private void addSellTaxUnit(BuySellEntry t, Map<String, String> v) { if (v.get("label").contains("im laufenden Jahr einbehaltene")) return; String currency = asCurrencyCode(v.get("currency")); // FIXME forex fees must update gross value if (currency.equals(t.getAccountTransaction().getCurrencyCode())) t.getPortfolioTransaction().addUnit(new Unit(Unit.Type.TAX, Money.of(currency, asAmount(v.get("tax"))))); } @SuppressWarnings("nls") private void addDividendTransaction() { DocumentType type = new DocumentType("Dividende"); this.addDocumentTyp(type); Block block = new Block("^Dividendengutschrift .*$"); type.addBlock(block); block.set(new Transaction<AccountTransaction>() .subject(() -> { AccountTransaction entry = new AccountTransaction(); entry.setType(AccountTransaction.Type.DIVIDENDS); return entry; }) .section("isin", "name", "currency") // .find("Gattungsbezeichnung ISIN") // .match("^(?<name>.*) (?<isin>[^ ]*)$") // .match("STK ([\\d.]+(,\\d+)?) (\\d+.\\d+.\\d{4}+) (\\d+.\\d+.\\d{4}+) (?<currency>\\w{3}+) (\\d+,\\d+)") .assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))) .section("shares") // .find("^Nominal Ex-Tag Zahltag .*") // .match("^STK (?<shares>[\\d.]+(,\\d+)?) .*$") .assign((t, v) -> t.setShares(asShares(v.get("shares")))) .section("date", "amount", "currency") // .optional() // .find("Wert *Konto-Nr. *Betrag *zu *Ihren *Gunsten") .match("^(?<date>\\d+.\\d+.\\d{4}+) ([0-9]*) (?<currency>\\w{3}+) (?<amount>[\\d.]+,\\d+)$") .assign((t, v) -> { t.setDate(asDate(v.get("date"))); t.setAmount(asAmount(v.get("amount"))); t.setCurrencyCode(asCurrencyCode(v.get("currency"))); }) .section("date", "amount", "currency", "forexCurrency", "exchangeRate") // .optional() // .find("Wert Konto-Nr. Devisenkurs Betrag zu Ihren Gunsten") .match("^(?<date>\\d+.\\d+.\\d{4}+) ([0-9]*) \\w{3}+/(?<forexCurrency>\\w{3}+) (?<exchangeRate>[\\d.]+,\\d+) (?<currency>\\w{3}+) (?<amount>[\\d.]+,\\d+)$") .assign((t, v) -> { t.setDate(asDate(v.get("date"))); t.setAmount(asAmount(v.get("amount"))); t.setCurrencyCode(asCurrencyCode(v.get("currency"))); BigDecimal exchangeRate = asExchangeRate(v.get("exchangeRate")).setScale(10, BigDecimal.ROUND_HALF_DOWN); Money forex = Money.of(asCurrencyCode(v.get("forexCurrency")), Math.round(t.getAmount() / exchangeRate.doubleValue())); Unit unit = new Unit(Unit.Type.GROSS_VALUE, t.getMonetaryAmount(), forex, exchangeRate); if (unit.getForex().getCurrencyCode().equals(t.getSecurity().getCurrencyCode())) t.addUnit(unit); }) // this section is needed, if the dividend is payed in // the forex currency to a account in forex curreny but // the security is listed in local currency .section("forex", "localCurrency", "forexCurrency", "exchangeRate") // .optional() // .find("Wert Konto-Nr. Betrag zu Ihren Gunsten") .match("^(\\d+.\\d+.\\d{4}+) ([0-9]*) (\\w{3}+) (?<forex>[\\d.]+,\\d+)$") .match("Devisenkurs: (?<localCurrency>\\w{3}+)/(?<forexCurrency>\\w{3}+) (?<exchangeRate>[\\d.]+,\\d+)") .assign((t, v) -> { BigDecimal exchangeRate = asExchangeRate(v.get("exchangeRate")).setScale(10, BigDecimal.ROUND_HALF_DOWN); Money forex = Money.of(asCurrencyCode(v.get("forexCurrency")), asAmount(v.get("forex"))); Money localAmount = Money.of(v.get("localCurrency"), Math.round(forex.getAmount() / Double.parseDouble(v.get("exchangeRate").replace(',', '.')))); t.setAmount(forex.getAmount()); t.setCurrencyCode(forex.getCurrencyCode()); Unit unit = new Unit(Unit.Type.GROSS_VALUE, forex, localAmount, exchangeRate); if (unit.getForex().getCurrencyCode().equals(t.getSecurity().getCurrencyCode())) t.addUnit(unit); }) .wrap(t -> { if (t.getAmount() == 0) throw new IllegalArgumentException("No dividend amount found."); return new TransactionItem(t); })); } @Override public String getLabel() { return "DAB Bank"; //$NON-NLS-1$ } }