package name.abuchen.portfolio.ui.dialogs.transactions; import java.math.BigDecimal; import java.time.LocalDate; import org.eclipse.core.databinding.validation.ValidationStatus; import org.eclipse.core.runtime.IStatus; import com.ibm.icu.text.MessageFormat; import name.abuchen.portfolio.model.Client; import name.abuchen.portfolio.model.Portfolio; import name.abuchen.portfolio.model.PortfolioTransferEntry; import name.abuchen.portfolio.model.Security; import name.abuchen.portfolio.model.Transaction; import name.abuchen.portfolio.model.TransactionOwner; import name.abuchen.portfolio.money.CurrencyConverter; import name.abuchen.portfolio.money.CurrencyConverterImpl; import name.abuchen.portfolio.money.Values; import name.abuchen.portfolio.snapshot.ClientSnapshot; import name.abuchen.portfolio.snapshot.PortfolioSnapshot; import name.abuchen.portfolio.snapshot.SecurityPosition; import name.abuchen.portfolio.ui.Messages; public class SecurityTransferModel extends AbstractModel { public enum Properties { security, securityCurrencyCode, sourcePortfolio, sourcePortfolioLabel, targetPortfolio, targetPortfolioLabel, date, shares, quote, amount, note, calculationStatus; } private final Client client; private PortfolioTransferEntry source; private Security security; private Portfolio sourcePortfolio; private Portfolio targetPortfolio; private LocalDate date = LocalDate.now(); private long shares; private BigDecimal quote = BigDecimal.ONE; private long amount; private String note; private IStatus calculationStatus = ValidationStatus.ok(); public SecurityTransferModel(Client client) { this.client = client; } @Override public String getHeading() { return Messages.LabelSecurityTransfer; } @Override public void applyChanges() { if (security == null) throw new UnsupportedOperationException(Messages.MsgMissingSecurity); if (sourcePortfolio == null) throw new UnsupportedOperationException(Messages.MsgPortfolioFromMissing); if (targetPortfolio == null) throw new UnsupportedOperationException(Messages.MsgPortfolioToMissing); PortfolioTransferEntry t; if (source != null && sourcePortfolio.equals(source.getOwner(source.getSourceTransaction())) && targetPortfolio.equals(source.getOwner(source.getTargetTransaction()))) { // transaction stays in same accounts t = source; } else { if (source != null) { @SuppressWarnings("unchecked") TransactionOwner<Transaction> owner = (TransactionOwner<Transaction>) source .getOwner(source.getSourceTransaction()); owner.deleteTransaction(source.getSourceTransaction(), client); source = null; } t = new PortfolioTransferEntry(sourcePortfolio, targetPortfolio); t.insert(); } t.setSecurity(security); t.setDate(date); t.setShares(shares); t.setAmount(amount); t.setCurrencyCode(security.getCurrencyCode()); t.setNote(note); } @Override public void resetToNewTransaction() { this.source = null; setShares(0); setAmount(0); setNote(null); } private IStatus calculateStatus() { // check whether gross value is in range long lower = Math.round(shares * quote.add(BigDecimal.valueOf(-0.01)).doubleValue() * Values.Amount.factor() / Values.Share.divider()); long upper = Math.round(shares * quote.add(BigDecimal.valueOf(0.01)).doubleValue() * Values.Amount.factor() / Values.Share.divider()); if (amount < lower || amount > upper) return ValidationStatus.error(Messages.MsgIncorrectSubTotal); if (amount == 0L) return ValidationStatus.error(MessageFormat.format(Messages.MsgDialogInputRequired, Messages.ColumnAmount)); return ValidationStatus.ok(); } private void updateSharesAndQuote() { SecurityPosition position = null; if (security != null) { CurrencyConverter converter = new CurrencyConverterImpl(getExchangeRateProviderFactory(), client.getBaseCurrency()); PortfolioSnapshot snapshot = sourcePortfolio != null ? PortfolioSnapshot.create(sourcePortfolio, converter, date) : ClientSnapshot.create(client, converter, date).getJointPortfolio(); position = snapshot.getPositionsBySecurity().get(security); } if (position != null) { setShares(position.getShares()); // setAmount will also set quote setAmount(position.calculateValue().getAmount()); } else if (security != null) { setShares(0); setQuote(new BigDecimal(security.getSecurityPrice(date).getValue() / Values.Quote.divider())); } else { setShares(0); setQuote(BigDecimal.ZERO); } } public void setSource(PortfolioTransferEntry entry) { this.source = entry; this.sourcePortfolio = (Portfolio) entry.getOwner(entry.getSourceTransaction()); this.targetPortfolio = (Portfolio) entry.getOwner(entry.getTargetTransaction()); this.security = entry.getSourceTransaction().getSecurity(); this.date = entry.getSourceTransaction().getDate(); this.shares = entry.getSourceTransaction().getShares(); this.quote = entry.getSourceTransaction().getGrossPricePerShare().toBigDecimal(); this.amount = entry.getTargetTransaction().getAmount(); this.note = entry.getSourceTransaction().getNote(); } @Override public IStatus getCalculationStatus() { return calculationStatus; } public Security getSecurity() { return security; } public void setSecurity(Security security) { String oldCurrencyCode = getSecurityCurrencyCode(); firePropertyChange(Properties.security.name(), this.security, this.security = security); firePropertyChange(Properties.securityCurrencyCode.name(), oldCurrencyCode, getSecurityCurrencyCode()); updateSharesAndQuote(); } public Portfolio getSourcePortfolio() { return sourcePortfolio; } public void setSourcePortfolio(Portfolio portfolio) { String oldLabel = getSourcePortfolioLabel(); firePropertyChange(Properties.sourcePortfolio.name(), this.sourcePortfolio, this.sourcePortfolio = portfolio); firePropertyChange(Properties.sourcePortfolioLabel.name(), oldLabel, getSourcePortfolioLabel()); updateSharesAndQuote(); } public String getSourcePortfolioLabel() { return sourcePortfolio != null ? sourcePortfolio.getReferenceAccount().getName() : ""; //$NON-NLS-1$ } public Portfolio getTargetPortfolio() { return targetPortfolio; } public void setTargetPortfolio(Portfolio portfolio) { String oldLabel = getTargetPortfolioLabel(); firePropertyChange(Properties.targetPortfolio.name(), this.targetPortfolio, this.targetPortfolio = portfolio); firePropertyChange(Properties.targetPortfolioLabel.name(), oldLabel, getTargetPortfolioLabel()); } public String getTargetPortfolioLabel() { return targetPortfolio != null ? targetPortfolio.getReferenceAccount().getName() : ""; //$NON-NLS-1$ } public LocalDate getDate() { return date; } public void setDate(LocalDate date) { firePropertyChange(Properties.date.name(), this.date, this.date = date); updateSharesAndQuote(); } public long getShares() { return shares; } public void setShares(long shares) { firePropertyChange(Properties.shares.name(), this.shares, this.shares = shares); if (quote.doubleValue() != 0) { setAmount(Math.round(shares * quote.doubleValue() * Values.Amount.factor() / Values.Share.divider())); } else if (amount != 0 && shares != 0) { setQuote(new BigDecimal(amount * Values.Share.factor() / (shares * Values.Amount.divider()))); } firePropertyChange(Properties.calculationStatus.name(), this.calculationStatus, this.calculationStatus = calculateStatus()); } public BigDecimal getQuote() { return quote; } public void setQuote(BigDecimal quote) { firePropertyChange(Properties.quote.name(), this.quote, this.quote = quote); triggerAmount(Math.round(shares * quote.doubleValue() * Values.Amount.factor() / Values.Share.divider())); firePropertyChange(Properties.calculationStatus.name(), this.calculationStatus, this.calculationStatus = calculateStatus()); } public long getAmount() { return amount; } public void setAmount(long amount) { triggerAmount(amount); if (shares != 0) { BigDecimal newQuote = new BigDecimal(amount * Values.Share.factor() / (shares * Values.Amount.divider())); firePropertyChange(Properties.quote.name(), this.quote, this.quote = newQuote); } firePropertyChange(Properties.calculationStatus.name(), this.calculationStatus, this.calculationStatus = calculateStatus()); } public void triggerAmount(long amount) { firePropertyChange(Properties.amount.name(), this.amount, this.amount = amount); } public String getNote() { return note; } public void setNote(String note) { firePropertyChange(Properties.note.name(), this.note, this.note = note); } public String getSecurityCurrencyCode() { return security != null ? security.getCurrencyCode() : ""; //$NON-NLS-1$ } }