package name.abuchen.portfolio.datatransfer.actions;
import java.text.MessageFormat;
import java.util.EnumSet;
import java.util.Optional;
import java.util.Set;
import name.abuchen.portfolio.Messages;
import name.abuchen.portfolio.datatransfer.ImportAction;
import name.abuchen.portfolio.model.Account;
import name.abuchen.portfolio.model.AccountTransaction;
import name.abuchen.portfolio.model.AccountTransferEntry;
import name.abuchen.portfolio.model.BuySellEntry;
import name.abuchen.portfolio.model.Portfolio;
import name.abuchen.portfolio.model.PortfolioTransaction;
import name.abuchen.portfolio.model.PortfolioTransferEntry;
import name.abuchen.portfolio.model.Security;
import name.abuchen.portfolio.model.Transaction;
import name.abuchen.portfolio.model.Transaction.Unit;
import name.abuchen.portfolio.money.CurrencyUnit;
import name.abuchen.portfolio.money.Money;
import name.abuchen.portfolio.money.MoneyCollectors;
import name.abuchen.portfolio.money.Values;
public class CheckCurrenciesAction implements ImportAction
{
private static final Set<AccountTransaction.Type> TRANSACTIONS_WO_UNITS = EnumSet.of(AccountTransaction.Type.BUY,
AccountTransaction.Type.SELL, AccountTransaction.Type.TRANSFER_IN);
@Override
public Status process(Security security)
{
String currency = security.getCurrencyCode();
CurrencyUnit unit = CurrencyUnit.getInstance(currency);
return unit != null ? Status.OK_STATUS
: new Status(Status.Code.ERROR,
MessageFormat.format(Messages.MsgCheckUnsupportedCurrency, currency));
}
@Override
public Status process(AccountTransaction transaction, Account account)
{
if (!account.getCurrencyCode().equals(transaction.getCurrencyCode()))
return new Status(Status.Code.ERROR,
MessageFormat.format(Messages.MsgCheckTransactionCurrencyDoesNotMatchAccount,
transaction.getCurrencyCode(), account.getCurrencyCode()));
if (transaction.getSecurity() != null)
{
if (TRANSACTIONS_WO_UNITS.contains(transaction.getType()))
{
// for buy/sell and transfer out transactions, the units are
// maintained in the portfolio transaction, not the account
// transaction.
if (transaction.getUnits().findAny().isPresent())
return new Status(Status.Code.ERROR, MessageFormat
.format(Messages.MsgCheckTransactionMustNotHaveGrossAmount, transaction.getType()));
}
else
{
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)));
}
}
}
return Status.OK_STATUS;
}
@Override
public Status process(PortfolioTransaction transaction, Portfolio portfolio)
{
Security security = transaction.getSecurity();
if (security == null)
return new Status(Status.Code.ERROR,
MessageFormat.format(Messages.MsgCheckMissingSecurity, transaction.getType().toString()));
Status status = checkGrossValueAndUnitsAgainstSecurity(transaction);
if (status.getCode() != Status.Code.OK)
return status;
if (transaction.getType() == PortfolioTransaction.Type.DELIVERY_OUTBOUND
|| transaction.getType() == PortfolioTransaction.Type.SELL)
{
// 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()) //
.collect(MoneyCollectors.sum(transaction.getCurrencyCode()));
if (!transaction.getMonetaryAmount().isGreaterOrEqualThan(taxAndFees))
return new Status(Status.Code.ERROR, MessageFormat.format(Messages.MsgCheckTaxAndFeesTooHigh,
Values.Money.format(transaction.getMonetaryAmount()), Values.Money.format(taxAndFees)));
}
return Status.OK_STATUS;
}
@Override
public Status process(BuySellEntry entry, Account account, Portfolio portfolio)
{
AccountTransaction t = entry.getAccountTransaction();
Status status = process(t, account);
if (status.getCode() != Status.Code.OK)
return status;
return process(entry.getPortfolioTransaction(), portfolio);
}
@Override
public Status process(AccountTransferEntry entry, Account source, Account target)
{
AccountTransaction t = entry.getSourceTransaction();
Status status = process(t, source);
if (status.getCode() != Status.Code.OK)
return status;
return process(entry.getTargetTransaction(), target);
}
@Override
public Status process(PortfolioTransferEntry entry, Portfolio source, Portfolio target)
{
PortfolioTransaction t = entry.getSourceTransaction();
Status status = process(t, source);
if (status.getCode() != Status.Code.OK)
return status;
return process(entry.getTargetTransaction(), target);
}
private Status checkGrossValueAndUnitsAgainstSecurity(Transaction transaction)
{
String securityCurrency = transaction.getSecurity().getCurrencyCode();
if (securityCurrency == null)
return new Status(Status.Code.ERROR, Messages.MsgCheckSecurityWithoutCurrency);
if (transaction.getCurrencyCode().equals(securityCurrency))
{
// then gross value unit must not be set
Optional<Unit> grossValue = transaction.getUnit(Transaction.Unit.Type.GROSS_VALUE);
if (grossValue.isPresent())
{
String grossValueCurrencyCode = grossValue.get().getForex() != null
? grossValue.get().getForex().getCurrencyCode() : ""; //$NON-NLS-1$
return new Status(Status.Code.ERROR, MessageFormat.format(Messages.MsgCheckGrossValueUnitNotValid,
grossValueCurrencyCode, securityCurrency));
}
// then other units must not have any forex information
Optional<Unit> unit = transaction.getUnits().filter(u -> u.getForex() != null).findAny();
if (unit.isPresent())
return new Status(Status.Code.ERROR, MessageFormat.format(Messages.MsgCheckUnitForexNotValid,
Values.Money.format(unit.get().getForex())));
}
else
{
// then gross value must be set
Optional<Unit> grossValue = transaction.getUnit(Transaction.Unit.Type.GROSS_VALUE);
if (!grossValue.isPresent())
return new Status(Status.Code.ERROR, MessageFormat.format(Messages.MsgCheckGrossValueUnitMissing,
transaction.getCurrencyCode(), securityCurrency));
// then gross value forex must match security
String forex = grossValue.get().getForex() != null ? grossValue.get().getForex().getCurrencyCode() : null;
if (!securityCurrency.equals(forex))
return new Status(Status.Code.ERROR, MessageFormat.format(Messages.MsgCheckGrossValueUnitForexMismatch,
forex, securityCurrency));
// then other units must have matching currency (if they have forex)
Optional<Unit> unit = transaction.getUnits() //
.filter(u -> u.getForex() != null) //
.filter(u -> !u.getForex().getCurrencyCode().equals(securityCurrency)) //
.findAny();
if (unit.isPresent())
return new Status(Status.Code.ERROR, MessageFormat.format(Messages.MsgCheckUnitForexMismatch,
Values.Money.format(unit.get().getForex()), securityCurrency));
}
return Status.OK_STATUS;
}
}