From 10cba96b5666291a79fc0f55288378cbd75f1b29 Mon Sep 17 00:00:00 2001 From: Andreas Buchen Date: Sun, 15 Mar 2020 09:40:53 +0100 Subject: [PATCH] Allow transaction type to be defined by regex pattern --- ...eckCurrenciesPortfolioTransactionTest.java | 7 +- .../actions/CheckCurrenciesAction.java | 6 +- .../csv/CSVAccountTransactionExtractor.java | 41 +++++- .../datatransfer/csv/CSVImporter.java | 19 ++- .../datatransfer/csv/csv-config.json | 130 ++++++++++++++++++ 5 files changed, 190 insertions(+), 13 deletions(-) diff --git a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/actions/CheckCurrenciesPortfolioTransactionTest.java b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/actions/CheckCurrenciesPortfolioTransactionTest.java index 27d464c63b..24636f5de4 100644 --- a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/actions/CheckCurrenciesPortfolioTransactionTest.java +++ b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/actions/CheckCurrenciesPortfolioTransactionTest.java @@ -105,20 +105,21 @@ public void testTransactionTaxesAndFeesAddUpForOutboundDeliveries() t.addUnit(tax); assertThat(action.process(t, portfolio).getCode(), is(Status.Code.OK)); + t.setType(Type.DELIVERY_INBOUND); t.setMonetaryAmount(Money.of("EUR", 7_00)); assertThat(action.process(t, portfolio).getCode(), is(Status.Code.ERROR)); - t.setType(Type.DELIVERY_INBOUND); + t.setType(Type.DELIVERY_OUTBOUND); assertThat(action.process(t, portfolio).getCode(), is(Status.Code.OK)); - t.setType(Type.DELIVERY_OUTBOUND); + t.setType(Type.DELIVERY_INBOUND); t.removeUnit(tax); assertThat(action.process(t, portfolio).getCode(), is(Status.Code.OK)); t.setMonetaryAmount(Money.of("EUR", 3_00)); assertThat(action.process(t, portfolio).getCode(), is(Status.Code.ERROR)); - t.setType(Type.DELIVERY_INBOUND); + t.setType(Type.DELIVERY_OUTBOUND); assertThat(action.process(t, portfolio).getCode(), is(Status.Code.OK)); } diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/actions/CheckCurrenciesAction.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/actions/CheckCurrenciesAction.java index 0982bfc604..efed5cf343 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/actions/CheckCurrenciesAction.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/actions/CheckCurrenciesAction.java @@ -80,13 +80,13 @@ public Status process(PortfolioTransaction transaction, Portfolio portfolio) if (status.getCode() != Status.Code.OK) return status; - if (transaction.getType() == PortfolioTransaction.Type.DELIVERY_OUTBOUND - || transaction.getType() == PortfolioTransaction.Type.SELL) + if (transaction.getType() == PortfolioTransaction.Type.DELIVERY_INBOUND + || transaction.getType() == PortfolioTransaction.Type.BUY) { // tax + fees must be < than transaction amount Money taxAndFees = transaction.getUnits() // .filter(u -> u.getType() == Unit.Type.TAX || u.getType() == Unit.Type.FEE) // - .map(u -> u.getAmount()) // + .map(Unit::getAmount) // .collect(MoneyCollectors.sum(transaction.getCurrencyCode())); if (!transaction.getMonetaryAmount().isGreaterOrEqualThan(taxAndFees)) diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/csv/CSVAccountTransactionExtractor.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/csv/CSVAccountTransactionExtractor.java index 8e243107b2..f12e4e36a3 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/csv/CSVAccountTransactionExtractor.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/csv/CSVAccountTransactionExtractor.java @@ -47,6 +47,7 @@ fields.add(new AmountField("shares", Messages.CSVColumn_Shares).setOptional(true)); //$NON-NLS-1$ fields.add(new Field("note", Messages.CSVColumn_Note).setOptional(true)); //$NON-NLS-1$ fields.add(new AmountField("taxes", Messages.CSVColumn_Taxes).setOptional(true)); //$NON-NLS-1$ + fields.add(new AmountField("fees", Messages.CSVColumn_Fees).setOptional(true)); //$NON-NLS-1$ fields.add(new Field("account", Messages.CSVColumn_AccountName).setOptional(true)); //$NON-NLS-1$ fields.add(new Field("account2nd", Messages.CSVColumn_AccountName2nd).setOptional(true)); //$NON-NLS-1$ fields.add(new Field("portfolio", Messages.CSVColumn_PortfolioName).setOptional(true)); //$NON-NLS-1$ @@ -78,6 +79,7 @@ void extract(List items, String[] rawValues, Map field2col String note = getText(Messages.CSVColumn_Note, rawValues, field2column); Long shares = getShares(Messages.CSVColumn_Shares, rawValues, field2column); Long taxes = getAmount(Messages.CSVColumn_Taxes, rawValues, field2column); + Long fees = getAmount(Messages.CSVColumn_Fees, rawValues, field2column); Account account = getAccount(getClient(), rawValues, field2column); Account account2nd = getAccount(getClient(), rawValues, field2column, true); @@ -116,7 +118,27 @@ void extract(List items, String[] rawValues, Map field2col buySellEntry.setSecurity(security); buySellEntry.setDate(date); buySellEntry.setNote(note); - item = new BuySellEntryItem(buySellEntry); + + if (taxes != null && taxes.longValue() != 0) + buySellEntry.getPortfolioTransaction().addUnit(new Unit(Unit.Type.TAX, Money + .of(buySellEntry.getPortfolioTransaction().getCurrencyCode(), Math.abs(taxes)))); + + if (fees != null && fees.longValue() != 0) + buySellEntry.getPortfolioTransaction().addUnit(new Unit(Unit.Type.FEE, Money + .of(buySellEntry.getPortfolioTransaction().getCurrencyCode(), Math.abs(fees)))); + + if (buySellEntry.getPortfolioTransaction().getAmount() == 0L + && buySellEntry.getPortfolioTransaction().getType() == PortfolioTransaction.Type.SELL) + { + // convert to outbound delivery if amount is 0 + PortfolioTransaction tx = buySellEntry.getPortfolioTransaction(); + item = new TransactionItem(convertToOutboundDelivery(tx)); + } + else + { + item = new BuySellEntryItem(buySellEntry); + } + break; case DIVIDENDS: // NOSONAR // dividends must have a security @@ -138,7 +160,8 @@ void extract(List items, String[] rawValues, Map field2col t.setType(type); t.setAmount(Math.abs(amount.getAmount())); t.setCurrencyCode(amount.getCurrencyCode()); - if (type == Type.DIVIDENDS || type == Type.TAXES || type == Type.TAX_REFUND || type == Type.FEES || type == Type.FEES_REFUND) + if (type == Type.DIVIDENDS || type == Type.TAXES || type == Type.TAX_REFUND || type == Type.FEES + || type == Type.FEES_REFUND) t.setSecurity(security); t.setDateTime(date.withHour(0).withMinute(0)); t.setNote(note); @@ -159,6 +182,20 @@ void extract(List items, String[] rawValues, Map field2col items.add(item); } + private PortfolioTransaction convertToOutboundDelivery(PortfolioTransaction tx) + { + PortfolioTransaction delivery = new PortfolioTransaction(); + delivery.setType(PortfolioTransaction.Type.DELIVERY_OUTBOUND); + delivery.setDateTime(tx.getDateTime()); + delivery.setAmount(tx.getAmount()); + delivery.setCurrencyCode(tx.getCurrencyCode()); + delivery.setShares(tx.getShares()); + delivery.setSecurity(tx.getSecurity()); + delivery.setNote(tx.getNote()); + delivery.addUnits(tx.getUnits()); + return delivery; + } + private Type inferType(String[] rawValues, Map field2column, Security security, Money amount) throws ParseException { diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/csv/CSVImporter.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/csv/CSVImporter.java index 62e352eafd..1d9735905b 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/csv/CSVImporter.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/csv/CSVImporter.java @@ -41,6 +41,7 @@ import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import java.util.stream.Collectors; import org.apache.commons.csv.CSVFormat; @@ -492,15 +493,23 @@ public M parseObject(String source, ParsePosition pos) } } - // second: try partial matches + // second: try as pattern for (Map.Entry entry : enumMap.entrySet()) { - int p = source.indexOf(entry.getValue()); - if (p >= 0) + try { - pos.setIndex(source.length()); - return entry.getKey(); + Pattern p = Pattern.compile(entry.getValue()); + + if (p.matcher(source).find()) + { + pos.setIndex(source.length()); + return entry.getKey(); + } + } + catch (PatternSyntaxException e) + { + PortfolioLog.error(e); } } diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/csv/csv-config.json b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/csv/csv-config.json index 36acdce57b..2cce527cf0 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/csv/csv-config.json +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/csv/csv-config.json @@ -298,4 +298,134 @@ "label": "Wirtschaftsraum (Detail)" } ] +}, +{ + "delimiter": ";", + "encoding": "UTF-8", + "isFirstLineHeader": true, + "label": "ebase Kontoauszug", + "skipLines": 0, + "target": "account-transaction", + "columns": [ + { + "label": "Depotnummer" + }, + { + "label": "Depotposition" + }, + { + "field": "date", + "format": "yyyy-MM-dd", + "label": "Datum" + }, + { + "label": "Ref. Nr." + }, + { + "field": "date", + "format": "yyyy-MM-dd", + "label": "Buchungsdatum" + }, + { + "field": "type", + "format": "DEPOSIT=Einlage;REMOVAL=Entnahme;INTEREST=Zinsen;INTEREST_CHARGE=Zinsbelastung;DIVIDENDS=^Fondsertrag$;FEES=Gebühren;FEES_REFUND=Gebührenerstattung;TAXES=Steuern;TAX_REFUND=Steuerrückerstattung;BUY=Kauf|Ansparplan|Wiederanlage;SELL=Verkauf;TRANSFER_IN=Umbuchung (Eingang);TRANSFER_OUT=Umbuchung (Ausgang)", + "label": "Umsatzart" + }, + { + "label": "Teilumsatz" + }, + { + "field": "name", + "label": "Fonds" + }, + { + "field": "isin", + "label": "ISIN" + }, + { + "field": "value", + "format": "0.000,00", + "label": "Zahlungsbetrag in ZW" + }, + { + "field": "currency", + "label": "Zahlungswährung (ZW)" + }, + { + "field": "shares", + "format": "0.000,00", + "label": "Anteile" + }, + { + "label": "Abrechnungskurs in FW" + }, + { + "label": "Fondswährung (FW)" + }, + { + "label": "Kursdatum" + }, + { + "label": "Devisenkurs (ZW/FW)" + }, + { + "label": "Anlagebetrag in ZW" + }, + { + "label": "Vertriebsprovision in ZW (im Abrechnungskurs enthalten)" + }, + { + "label": "KVG Einbehalt in ZW (im Abrechnungskurs enthalten)" + }, + { + "label": "Gegenwert der Anteile in ZW" + }, + { + "label": "Anteile zum Bestandsdatum" + }, + { + "label": "Barausschüttung/Steuerliquidität je Anteil in EW" + }, + { + "label": "Ertragswährung (EW)" + }, + { + "label": "Bestandsdatum" + }, + { + "label": "Devisenkurs (ZW/EW)" + }, + { + "label": "Barausschüttung/Steuerliquidität in ZW" + }, + { + "label": "Entgelt in ZW" + }, + { + "field": "fees", + "format": "0.000,00", + "label": "Entgelt in EUR" + }, + { + "label": "Steuern in ZW" + }, + { + "field": "taxes", + "format": "0.000,00", + "label": "Steuern in EUR" + }, + { + "label": "Devisenkurs (EUR/ZW)" + }, + { + "label": "Art des Steuereinbehalts" + }, + { + "label": "Steuereinbehalt in EUR" + }, + { + "field": "note", + "label": "Orderinfo" + } + ] }] \ No newline at end of file