From 262be4cc3084c66f088797aa59fc403fe9fbd482 Mon Sep 17 00:00:00 2001 From: chrisaut Date: Tue, 29 Dec 2020 11:58:44 +0100 Subject: [PATCH] Allow user to select an explicit currency for dividends --- .../AccountTransactionDialog.java | 75 +++++++++++++++++-- .../transactions/AccountTransactionModel.java | 44 +++++++++-- 2 files changed, 106 insertions(+), 13 deletions(-) diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionDialog.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionDialog.java index 58ed08413d..f7ae139018 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionDialog.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionDialog.java @@ -10,6 +10,7 @@ import java.text.MessageFormat; import java.time.LocalDate; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import javax.annotation.PostConstruct; @@ -23,6 +24,7 @@ import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; import org.eclipse.jface.databinding.swt.typed.WidgetProperties; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; @@ -40,6 +42,7 @@ import name.abuchen.portfolio.model.Security; import name.abuchen.portfolio.money.CurrencyConverter; import name.abuchen.portfolio.money.CurrencyConverterImpl; +import name.abuchen.portfolio.money.CurrencyUnit; import name.abuchen.portfolio.money.ExchangeRateProviderFactory; import name.abuchen.portfolio.money.Values; import name.abuchen.portfolio.snapshot.ClientSnapshot; @@ -51,6 +54,7 @@ import name.abuchen.portfolio.ui.util.FormDataFactory; import name.abuchen.portfolio.ui.util.LabelOnly; import name.abuchen.portfolio.ui.util.SWTHelper; +import name.abuchen.portfolio.ui.util.SimpleAction; public class AccountTransactionDialog extends AbstractTransactionDialog // NOSONAR { @@ -62,6 +66,7 @@ public class AccountTransactionDialog extends AbstractTransactionDialog // NOSON private boolean useIndirectQuotation = false; private Menu contextMenu; + private Menu contextMenuCurrencies; @Inject public AccountTransactionDialog(@Named(IServiceConstants.ACTIVE_SHELL) Shell parentShell) @@ -136,6 +141,20 @@ public void widgetSelected(SelectionEvent e) Input dividendAmount = new Input(editArea, Messages.LabelDividendPerShare); dividendAmount.bindBigDecimal(Properties.dividendAmount.name(), "#,##0.0000"); //$NON-NLS-1$ dividendAmount.bindCurrency(Properties.fxCurrencyCode.name()); + + Button btnDividendCurrency = new Button(editArea, SWT.ARROW | SWT.DOWN); + btnDividendCurrency.setText(model().getFxCurrencyCode()); + btnDividendCurrency.addSelectionListener(new SelectionAdapter() + { + @Override + public void widgetSelected(SelectionEvent e) + { + showCurrencySelectionMenu(); + } + }); + + btnDividendCurrency.setVisible(model().supportsShares()); + dividendAmount.setVisible(model().supportsShares()); // other input fields @@ -171,7 +190,7 @@ public void widgetSelected(SelectionEvent e) Input forexFees = new Input(editArea, Messages.ColumnFees); forexFees.bindValue(Properties.fxFees.name(), Messages.ColumnFees, Values.Amount, false); - forexFees.bindCurrency(Properties.securityCurrencyCode.name()); + forexFees.bindCurrency(Properties.fxCurrencyCode.name()); forexFees.setVisible(model().supportsFees()); Input fees = new Input(editArea, Messages.ColumnFees); @@ -260,7 +279,8 @@ public void widgetSelected(SelectionEvent e) // shares [- amount per share] startingWith(btnShares).thenRight(dividendAmount.label) // .thenRight(dividendAmount.value).width(amountWidth) // - .thenRight(dividendAmount.currency).width(currencyWidth); // + .thenRight(dividendAmount.currency).width(currencyWidth) + .thenRight(btnDividendCurrency); // } forms = startingWith(grossAmount.value); @@ -303,12 +323,12 @@ public void widgetSelected(SelectionEvent e) // model.addPropertyChangeListener(Properties.exchangeRateCurrencies.name(), event -> { // NOSONAR - String securityCurrency = model().getSecurityCurrencyCode(); + String maybeFxCurrency = model().getFxCurrencyCode(); String accountCurrency = model().getAccountCurrencyCode(); // make exchange rate visible if both are set but different - boolean isFxVisible = securityCurrency.length() > 0 && accountCurrency.length() > 0 - && !securityCurrency.equals(accountCurrency); + boolean isFxVisible = maybeFxCurrency.length() > 0 && accountCurrency.length() > 0 + && !maybeFxCurrency.equals(accountCurrency); exchangeRate.setVisible(isFxVisible); grossAmount.setVisible(isFxVisible); @@ -377,6 +397,19 @@ private void showSharesContextMenu() contextMenu.setVisible(true); } + + private void showCurrencySelectionMenu() + { + if (contextMenuCurrencies == null) + { + MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$ + menuMgr.setRemoveAllWhenShown(true); + menuMgr.addMenuListener(this::currencyMenuAboutToShow); + contextMenuCurrencies = menuMgr.createContextMenu(getShell()); + } + + contextMenuCurrencies.setVisible(true); + } private void sharesMenuAboutToShow(IMenuManager manager) // NOSONAR { @@ -408,6 +441,36 @@ public void run() } }); } + + private void currencyMenuAboutToShow(IMenuManager manager) // NOSONAR + { + // put list of favorite units on top + List allUnits = client.getUsedCurrencies(); + // add a separator marker + allUnits.add(null); + // then all available units + List available = CurrencyUnit.getAvailableCurrencyUnits(); + Collections.sort(available); + allUnits.addAll(available); + // now show the list + for (final CurrencyUnit unit : allUnits) + { + // is this a unit or a separator? + if (unit != null) + { + Action action = new SimpleAction(unit.getLabel(), a -> { + model().setFxCurrencyCode(unit.getCurrencyCode()); + }); + action.setChecked(model().getFxCurrencyCode().equals(unit.getCurrencyCode())); + manager.add(action); + } + else + { + // add a separator + manager.add(new Separator()); + } + } + } private void addAction(IMenuManager manager, PortfolioSnapshot portfolio, final String label) { @@ -426,7 +489,7 @@ public void run() manager.add(action); } } - + private void widgetDisposed() { if (contextMenu != null && !contextMenu.isDisposed()) diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionModel.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionModel.java index 45e0b9c811..b726975750 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionModel.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionModel.java @@ -42,6 +42,7 @@ public enum Properties private Account sourceAccount; private AccountTransaction sourceTransaction; + private String explicitFxCurrencyCode; private Security security; private Account account; private LocalDate date = LocalDate.now(); @@ -149,7 +150,7 @@ public void applyChanges() { Transaction.Unit forex = new Transaction.Unit(Transaction.Unit.Type.GROSS_VALUE, // Money.of(getAccountCurrencyCode(), grossAmount), // - Money.of(getSecurityCurrencyCode(), fxGrossAmount), // + Money.of(fxCurrencyCode, fxGrossAmount), // getExchangeRate()); t.addUnit(forex); @@ -182,6 +183,7 @@ public void resetToNewTransaction() setFxTaxes(0); setNote(null); setTime(PresetValues.getTime()); + setFxCurrencyCode(null); } public boolean supportsShares() @@ -258,6 +260,10 @@ public void setSource(Account account, AccountTransaction transaction) this.exchangeRate = unit.getExchangeRate(); this.grossAmount = unit.getAmount().getAmount(); this.fxGrossAmount = unit.getForex().getAmount(); + if(!unit.getForex().getCurrencyCode().equals(security.getCurrencyCode())) + { + setFxCurrencyCode(unit.getForex().getCurrencyCode()); + } break; case TAX: if (unit.getForex() != null) @@ -335,6 +341,29 @@ public void setAccount(Account account) updateExchangeRate(); } + + public void setFxCurrencyCode(String currencyCode) + { + String oldFxCurrencyCode = getFxCurrencyCode(); + if(oldFxCurrencyCode.equals(currencyCode)) return; + + String oldExchangeRateCurrencies = getExchangeRateCurrencies(); + String oldInverseExchangeRateCurrencies = getInverseExchangeRateCurrencies(); + + this.explicitFxCurrencyCode = null; + if(!getFxCurrencyCode().equals(currencyCode)) + { + this.explicitFxCurrencyCode = currencyCode; + } + + firePropertyChange(Properties.fxCurrencyCode.name(), oldFxCurrencyCode, getFxCurrencyCode()); + firePropertyChange(Properties.exchangeRateCurrencies.name(), oldExchangeRateCurrencies, + getExchangeRateCurrencies()); + firePropertyChange(Properties.inverseExchangeRateCurrencies.name(), oldInverseExchangeRateCurrencies, + getInverseExchangeRateCurrencies()); + + updateExchangeRate(); + } public Security getSecurity() { @@ -367,7 +396,7 @@ public void setSecurity(Security security) private void updateExchangeRate() { // set exchange rate to 1, if account and security have the same currency or no security is selected - if (getAccountCurrencyCode().equals(getSecurityCurrencyCode()) || getSecurityCurrencyCode().isEmpty()) + if (getAccountCurrencyCode().equals(getFxCurrencyCode()) || getFxCurrencyCode().isEmpty()) { setExchangeRate(BigDecimal.ONE); return; @@ -378,10 +407,10 @@ private void updateExchangeRate() if (sourceTransaction != null) return; - if (!getSecurityCurrencyCode().isEmpty()) + if (!getFxCurrencyCode().isEmpty()) { ExchangeRateTimeSeries series = getExchangeRateProviderFactory() // - .getTimeSeries(getSecurityCurrencyCode(), getAccountCurrencyCode()); + .getTimeSeries(getFxCurrencyCode(), getAccountCurrencyCode()); if (series != null) setExchangeRate(series.lookupRate(date).orElse(new ExchangeRate(date, BigDecimal.ONE)).getValue()); @@ -675,16 +704,17 @@ public String getSecurityCurrencyCode() public String getFxCurrencyCode() { + if(explicitFxCurrencyCode != null) return explicitFxCurrencyCode; return security != null && !security.getCurrencyCode().isEmpty() ? security.getCurrencyCode() : getAccountCurrencyCode(); } - + /** * Returns exchange rate label in direct (price) notation. */ public String getExchangeRateCurrencies() { - return String.format("%s/%s", getSecurityCurrencyCode(), getAccountCurrencyCode()); //$NON-NLS-1$ + return String.format("%s/%s", getFxCurrencyCode(), getAccountCurrencyCode()); //$NON-NLS-1$ } /** @@ -692,7 +722,7 @@ public String getExchangeRateCurrencies() */ public String getInverseExchangeRateCurrencies() { - return String.format("%s/%s", getAccountCurrencyCode(), getSecurityCurrencyCode()); //$NON-NLS-1$ + return String.format("%s/%s", getAccountCurrencyCode(), getFxCurrencyCode()); //$NON-NLS-1$ } public AccountTransaction.Type getType()