package name.abuchen.portfolio.datatransfer.csv; import java.text.MessageFormat; import java.text.ParseException; import java.time.LocalDate; import java.util.List; import java.util.Map; import java.util.StringJoiner; import name.abuchen.portfolio.Messages; import name.abuchen.portfolio.datatransfer.csv.CSVImporter.AmountField; import name.abuchen.portfolio.datatransfer.csv.CSVImporter.Column; import name.abuchen.portfolio.datatransfer.csv.CSVImporter.DateField; import name.abuchen.portfolio.datatransfer.csv.CSVImporter.EnumField; import name.abuchen.portfolio.datatransfer.csv.CSVImporter.Field; import name.abuchen.portfolio.model.AccountTransaction; import name.abuchen.portfolio.model.AccountTransaction.Type; import name.abuchen.portfolio.model.AccountTransferEntry; import name.abuchen.portfolio.model.BuySellEntry; import name.abuchen.portfolio.model.Client; import name.abuchen.portfolio.model.PortfolioTransaction; import name.abuchen.portfolio.model.Security; import name.abuchen.portfolio.model.Transaction.Unit; import name.abuchen.portfolio.money.Money; /* package */ class CSVAccountTransactionExtractor extends BaseCSVExtractor { /* package */ CSVAccountTransactionExtractor(Client client) { super(client, Messages.CSVDefAccountTransactions); List<Field> fields = getFields(); fields.add(new DateField(Messages.CSVColumn_Date)); fields.add(new Field(Messages.CSVColumn_ISIN).setOptional(true)); fields.add(new Field(Messages.CSVColumn_TickerSymbol).setOptional(true)); fields.add(new Field(Messages.CSVColumn_WKN).setOptional(true)); fields.add(new AmountField(Messages.CSVColumn_Value)); fields.add(new Field(Messages.CSVColumn_TransactionCurrency).setOptional(true)); fields.add(new EnumField<AccountTransaction.Type>(Messages.CSVColumn_Type, Type.class).setOptional(true)); fields.add(new Field(Messages.CSVColumn_SecurityName).setOptional(true)); fields.add(new AmountField(Messages.CSVColumn_Shares).setOptional(true)); fields.add(new Field(Messages.CSVColumn_Note).setOptional(true)); fields.add(new AmountField(Messages.CSVColumn_Taxes).setOptional(true)); } @Override void extract(List<Item> items, String[] rawValues, Map<String, Column> field2column) throws ParseException { // check if we have a security Security security = getSecurity(rawValues, field2column, s -> s.setCurrencyCode( getCurrencyCode(Messages.CSVColumn_TransactionCurrency, rawValues, field2column))); // check for the transaction amount Money amount = getMoney(rawValues, field2column); // determine type (if not explicitly given by import) Type type = inferType(rawValues, field2column, security, amount); // extract remaining fields LocalDate date = getDate(Messages.CSVColumn_Date, rawValues, field2column); if (date == null) throw new ParseException(MessageFormat.format(Messages.CSVImportMissingField, Messages.CSVColumn_Date), 0); String note = getText(Messages.CSVColumn_Note, rawValues, field2column); Long shares = getShares(Messages.CSVColumn_Shares, rawValues, field2column); Long taxes = getAmount(Messages.CSVColumn_Taxes, rawValues, field2column); switch (type) { case TRANSFER_IN: case TRANSFER_OUT: AccountTransferEntry entry = new AccountTransferEntry(); entry.setAmount(Math.abs(amount.getAmount())); entry.setCurrencyCode(amount.getCurrencyCode()); entry.setDate(date); entry.setNote(note); items.add(new AccountTransferItem(entry, type == Type.TRANSFER_OUT)); break; case BUY: case SELL: if (security == null) throw new ParseException(MessageFormat.format(Messages.CSVImportMissingSecurity, new StringJoiner(", ").add(Messages.CSVColumn_ISIN) //$NON-NLS-1$ .add(Messages.CSVColumn_TickerSymbol).add(Messages.CSVColumn_WKN) .toString()), 0); if (shares == null) throw new ParseException( MessageFormat.format(Messages.CSVImportMissingField, Messages.CSVColumn_Shares), 0); BuySellEntry buySellEntry = new BuySellEntry(); buySellEntry.setType(PortfolioTransaction.Type.valueOf(type.name())); buySellEntry.setAmount(Math.abs(amount.getAmount())); buySellEntry.setShares(Math.abs(shares)); buySellEntry.setCurrencyCode(amount.getCurrencyCode()); buySellEntry.setSecurity(security); buySellEntry.setDate(date); buySellEntry.setNote(note); items.add(new BuySellEntryItem(buySellEntry)); break; case DIVIDENDS: if (security == null) throw new ParseException(MessageFormat.format(Messages.CSVImportMissingSecurity, new StringJoiner(", ").add(Messages.CSVColumn_ISIN) //$NON-NLS-1$ .add(Messages.CSVColumn_TickerSymbol).add(Messages.CSVColumn_WKN) .toString()), 0); case DEPOSIT: case TAXES: case TAX_REFUND: case FEES: case FEES_REFUND: case INTEREST: case INTEREST_CHARGE: case REMOVAL: AccountTransaction t = new AccountTransaction(); t.setType(type); t.setAmount(Math.abs(amount.getAmount())); t.setCurrencyCode(amount.getCurrencyCode()); if (type == Type.DIVIDENDS || type == Type.TAX_REFUND) t.setSecurity(security); t.setDate(date); t.setNote(note); if (shares != null && type == Type.DIVIDENDS) t.setShares(Math.abs(shares)); if (type == Type.DIVIDENDS && taxes != null && taxes.longValue() != 0) t.addUnit(new Unit(Unit.Type.TAX, Money.of(t.getCurrencyCode(), Math.abs(taxes)))); items.add(new TransactionItem(t)); break; default: throw new IllegalArgumentException(type.toString()); } } private Type inferType(String[] rawValues, Map<String, Column> field2column, Security security, Money amount) throws ParseException { Type type = getEnum(Messages.CSVColumn_Type, Type.class, rawValues, field2column); if (type == null) { if (security != null) type = amount.isNegative() ? AccountTransaction.Type.REMOVAL : AccountTransaction.Type.DIVIDENDS; else type = amount.isNegative() ? AccountTransaction.Type.REMOVAL : AccountTransaction.Type.DEPOSIT; } return type; } }