Skip to content

Commit

Permalink
Import and export taxes on dividends via CSV
Browse files Browse the repository at this point in the history
Issue: #574
  • Loading branch information
buchen committed Jul 17, 2016
1 parent 9a54cff commit 65c9317
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import name.abuchen.portfolio.model.BuySellEntry;
import name.abuchen.portfolio.model.Client;
import name.abuchen.portfolio.model.Security;
import name.abuchen.portfolio.model.Transaction.Unit;
import name.abuchen.portfolio.money.CurrencyUnit;
import name.abuchen.portfolio.money.Money;
import name.abuchen.portfolio.money.Values;
Expand Down Expand Up @@ -345,4 +346,28 @@ public void testRequiredFieldAmount()
assertThat(results, empty());
assertThat(errors.size(), is(1));
}

@Test
public void testTaxesOnDividends()
{
Client client = new Client();

CSVExtractor extractor = new CSVAccountTransactionExtractor(client);

List<Exception> errors = new ArrayList<Exception>();
List<Item> results = extractor.extract(0,
Arrays.<String[]>asList( //
new String[] { "2013-01-01", "DE0007164600", "SAP.DE", "", "100", "EUR",
"DIVIDENDS", "SAP SE", "10", "Notiz", "10" }),
buildField2Column(extractor), errors);

assertThat(results.size(), is(2));
new AssertImportActions().check(results, CurrencyUnit.EUR);

AccountTransaction t = (AccountTransaction) results.stream().filter(i -> i instanceof TransactionItem).findAny()
.get().getSubject();
assertThat(t.getType(), is(AccountTransaction.Type.DIVIDENDS));
assertThat(t.getMonetaryAmount(), is(Money.of(CurrencyUnit.EUR, 100_00)));
assertThat(t.getUnitSum(Unit.Type.TAX), is(Money.of(CurrencyUnit.EUR, 10_00)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,22 @@ public Status process(AccountTransaction transaction, Account account)
Status status = checkGrossValueAndUnitsAgainstSecurity(transaction);
if (status.getCode() != Status.Code.OK)
return status;

if (transaction.getType() == AccountTransaction.Type.DIVIDENDS)
{
// tax must be < than transaction amount
Money taxes = transaction.getUnits() //
.filter(u -> u.getType() == Unit.Type.TAX) //
.map(u -> u.getAmount()) //
.collect(MoneyCollectors.sum(transaction.getCurrencyCode()));

if (!transaction.getMonetaryAmount().isGreaterOrEqualThan(taxes))
return new Status(Status.Code.ERROR,
MessageFormat.format(Messages.MsgCheckTaxAndFeesTooHigh,
Values.Money.format(transaction.getMonetaryAmount()),
Values.Money.format(taxes)));
}

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
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
Expand All @@ -39,15 +40,15 @@
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));
});
Security security = getSecurity(rawValues, field2column, s -> s.setCurrencyCode(
getCurrencyCode(Messages.CSVColumn_TransactionCurrency, rawValues, field2column)));

// check for the transaction amount
Money amount = getMoney(rawValues, field2column);
Expand All @@ -61,6 +62,7 @@ void extract(List<Item> items, String[] rawValues, Map<String, Column> field2col
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)
{
Expand Down Expand Up @@ -118,6 +120,8 @@ void extract(List<Item> items, String[] rawValues, Map<String, Column> field2col
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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public void exportAccountTransactions(File file, Account account) throws IOExcep
Messages.CSVColumn_Type, //
Messages.CSVColumn_Value, //
Messages.CSVColumn_TransactionCurrency, //
Messages.CSVColumn_Taxes, //
Messages.CSVColumn_Shares, //
Messages.CSVColumn_ISIN, //
Messages.CSVColumn_WKN, //
Expand All @@ -57,6 +58,8 @@ public void exportAccountTransactions(File file, Account account) throws IOExcep
printer.print(t.getType().toString());
printer.print(Values.Amount.format(t.getType().isDebit() ? -t.getAmount() : t.getAmount()));
printer.print(t.getCurrencyCode());
printer.print(t.getType() == AccountTransaction.Type.DIVIDENDS
? Values.Amount.format(t.getUnitSum(Unit.Type.TAX).getAmount()) : ""); //$NON-NLS-1$
printer.print(t.getShares() != 0 ? Values.Share.format(t.getShares()) : ""); //$NON-NLS-1$

printSecurityInfo(printer, t);
Expand Down

0 comments on commit 65c9317

Please sign in to comment.