diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/Messages.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/Messages.java index 074adfbffd..22e8678fc2 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/Messages.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/Messages.java @@ -1222,6 +1222,9 @@ public class Messages extends NLS public static String OptionDateIsInTheFuture; public static String OptionDateIsInThePast; public static String YearlyPerformanceHeatmapToolTip; + public static String nonCashEffective; + public static String LabelnonCashEffective; + public static String MsgInfoNonCashEffective; static { // initialize resource bundle 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..e1edf8f449 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 @@ -27,6 +27,7 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; @@ -45,12 +46,14 @@ import name.abuchen.portfolio.snapshot.ClientSnapshot; import name.abuchen.portfolio.snapshot.PortfolioSnapshot; import name.abuchen.portfolio.snapshot.SecurityPosition; +import name.abuchen.portfolio.ui.Images; import name.abuchen.portfolio.ui.Messages; import name.abuchen.portfolio.ui.UIConstants; import name.abuchen.portfolio.ui.dialogs.transactions.AccountTransactionModel.Properties; 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.swt.ControlDecoration; public class AccountTransactionDialog extends AbstractTransactionDialog // NOSONAR { @@ -204,6 +207,25 @@ public void widgetSelected(SelectionEvent e) total.bindCurrency(Properties.accountCurrencyCode.name()); total.setVisible(model().supportsTaxUnits() || model().supportsFees()); + // cash effectiveness + + Label labelnonCashEffective = new Label(editArea, SWT.LEFT); + labelnonCashEffective.setText(Messages.nonCashEffective); + Button buttonnonCashEffective = new Button(editArea, SWT.CHECK); + IObservableValue targetnonCashEffective = WidgetProperties.buttonSelection().observe(buttonnonCashEffective); + IObservableValue modelnonCashEffective = BeanProperties.value(Properties.nonCashEffective.name()) + .observe(model); + context.bindValue(targetnonCashEffective, modelnonCashEffective); + buttonnonCashEffective.setVisible(model().supportsnonCashEffective()); + labelnonCashEffective.setVisible(model().supportsnonCashEffective()); + + Image info = Images.INFO.image(); + ControlDecoration deco = new ControlDecoration(buttonnonCashEffective, SWT.CENTER | SWT.LEFT); + deco.setDescriptionText(Messages.MsgInfoNonCashEffective); + deco.setImage(info); + deco.setMarginWidth(2); + deco.show(); + // note Label lblNote = new Label(editArea, SWT.LEFT); @@ -237,7 +259,6 @@ public void widgetSelected(SelectionEvent e) int currencyWidth = currencyWidth(fxGrossAmount.currency); // date - // shares forms = forms.thenBelow(dateTime.date.getControl()).label(dateTime.label); startingWith(dateTime.date.getControl()).thenRight(dateTime.time).thenRight(dateTime.button, 0); @@ -294,6 +315,14 @@ public void widgetSelected(SelectionEvent e) .width(currencyWidth); } + // cash effectiveness + if (model().supportsnonCashEffective()) + { + forms = forms.thenBelow(buttonnonCashEffective).height(SWTHelper.lineHeight(buttonnonCashEffective) * 2) + .left(accounts.value.getControl()); + startingWith(buttonnonCashEffective).thenRight(labelnonCashEffective); + } + // note forms.thenBelow(valueNote).height(SWTHelper.lineHeight(valueNote) * 3).left(accounts.value.getControl()) .right(grossAmount.value).label(lblNote); 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..8e029cd7d5 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 @@ -31,7 +31,7 @@ public enum Properties { security, account, date, time, shares, fxGrossAmount, dividendAmount, exchangeRate, inverseExchangeRate, grossAmount, // NOSONAR fxTaxes, taxes, fxFees, fees, total, note, exchangeRateCurrencies, inverseExchangeRateCurrencies, // NOSONAR - accountCurrencyCode, securityCurrencyCode, fxCurrencyCode, calculationStatus; // NOSONAR + accountCurrencyCode, securityCurrencyCode, fxCurrencyCode, calculationStatus, nonCashEffective; // NOSONAR } public static final Security EMPTY_SECURITY = new Security("-----", ""); //$NON-NLS-1$ //$NON-NLS-2$ @@ -60,9 +60,11 @@ public enum Properties private long total; private String note; + private boolean nonCashEffective; private IStatus calculationStatus = ValidationStatus.ok(); + public AccountTransactionModel(Client client, AccountTransaction.Type type) { this.client = client; @@ -134,6 +136,7 @@ public void applyChanges() t.setShares(supportsShares() ? shares : 0); t.setAmount(total); t.setType(type); + t.setnonCashEffective(nonCashEffective); t.setNote(note); t.clearUnits(); @@ -181,6 +184,7 @@ public void resetToNewTransaction() setTaxes(0); setFxTaxes(0); setNote(null); + setnonCashEffective(false); setTime(PresetValues.getTime()); } @@ -228,6 +232,11 @@ public boolean supportsFees() return type == AccountTransaction.Type.DIVIDENDS; } + public boolean supportsnonCashEffective() + { + return type == AccountTransaction.Type.DIVIDENDS; + } + public void setSource(Account account, AccountTransaction transaction) { this.sourceAccount = account; @@ -285,6 +294,8 @@ public void setSource(Account account, AccountTransaction transaction) this.dividendAmount = calculateDividendAmount(); this.note = transaction.getNote(); + + this.nonCashEffective = transaction.getnonCashEffective(); } @Override @@ -663,6 +674,17 @@ public void setNote(String note) firePropertyChange(Properties.note.name(), this.note, this.note = note); } + public boolean getnonCashEffective() + { + return nonCashEffective; + } + + public void setnonCashEffective(boolean nonCashEffective) + { + firePropertyChange(Properties.nonCashEffective.name(), this.nonCashEffective, + this.nonCashEffective = nonCashEffective); + } + public String getAccountCurrencyCode() { return account != null ? account.getCurrencyCode() : ""; //$NON-NLS-1$ diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/messages.properties b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/messages.properties index 91894c3ed6..fe089aeeae 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/messages.properties +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/messages.properties @@ -2435,3 +2435,9 @@ WatchlistRename = Rename Watchlist Website = Website YearlyPerformanceHeatmapToolTip = Yearly rate of return displayed as heatmap\n\nTo calculate the yearly rate of return, the true-time weighted rate of return (TTWROR) is used.\n\nThe rate of return is always calculated for the full year - even if the reporting interval ends or starts in the middle of a year. + +nonCashEffective = non cash-effective dividend + +LabelnonCashEffective = Exclude non cash-effective dividends + +MsgInfoNonCashEffective = Marking a dividend payment as non cash-effective allows the dividend to be excluded in the payments view. \nThis option can be used e.g. for stock dividends or spin offs. \ No newline at end of file diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/messages_de.properties b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/messages_de.properties index 7a7a47afab..7deec1a31e 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/messages_de.properties +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/messages_de.properties @@ -2418,3 +2418,9 @@ WatchlistRename = Watchliste umbenennen Website = Website YearlyPerformanceHeatmapToolTip = Jahresrenditen in einer Heatmap\n\nZur Berechnung der Jahresrendite wird der True-Time Weighted Rate of Return (TTWROR) herangezogen.\n\nDie Rendite bezieht sich immer auf das gesamte Jahr - selbst wenn der Berichtszeitraum in der Mitte des Jahres beginnt bzw. endet. + +nonCashEffective = nicht-zahlungswirksame Dividende + +LabelnonCashEffective = Nicht-zahlungswirksame Dividenden ausschlie\u00DFen + +MsgInfoNonCashEffective = Eine Dividendenzahlung als nicht-zahlungswirksam zu markieren erlaubt es, die Dividende in der Ansicht Zahlungen auszuschlie\u00DFen. \nDiese Option kann z.B. bei Aktien Dividenden oder Spin Offs verwendet werden. \ No newline at end of file diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/payments/PaymentsView.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/payments/PaymentsView.java index c154d30919..b98c0c5bd2 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/payments/PaymentsView.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/payments/PaymentsView.java @@ -27,13 +27,13 @@ import name.abuchen.portfolio.ui.editor.AbstractFinanceView; import name.abuchen.portfolio.ui.util.DropDown; import name.abuchen.portfolio.ui.util.SimpleAction; -import name.abuchen.portfolio.ui.views.payments.PaymentsViewModel.Mode; import name.abuchen.portfolio.ui.views.panes.HistoricalPricesPane; import name.abuchen.portfolio.ui.views.panes.InformationPanePage; import name.abuchen.portfolio.ui.views.panes.SecurityEventsPane; import name.abuchen.portfolio.ui.views.panes.SecurityPriceChartPane; import name.abuchen.portfolio.ui.views.panes.TradesPane; import name.abuchen.portfolio.ui.views.panes.TransactionsPane; +import name.abuchen.portfolio.ui.views.payments.PaymentsViewModel.Mode; import name.abuchen.portfolio.util.TextUtil; public class PaymentsView extends AbstractFinanceView @@ -44,6 +44,8 @@ public class PaymentsView extends AbstractFinanceView private static final String KEY_USE_GROSS_VALUE = PaymentsView.class.getSimpleName() + "-use-gross-value"; //$NON-NLS-1$ private static final String KEY_USE_CONSOLIDATE_RETIRED = PaymentsView.class.getSimpleName() + "-use-consolidate-retired"; //$NON-NLS-1$ + private static final String KEY_EXCLUDE_NON_CASH_EFFECTIVE = PaymentsView.class.getSimpleName() + + "-exclude-non-cash-effective"; //$NON-NLS-1$ @Inject private Client client; @@ -86,14 +88,16 @@ public void setupModel() boolean useGrossValue = preferences.getBoolean(KEY_USE_GROSS_VALUE); boolean useConsolidateRetired = preferences.getBoolean(KEY_USE_CONSOLIDATE_RETIRED); + boolean excludenonCashEffective = preferences.getBoolean(KEY_EXCLUDE_NON_CASH_EFFECTIVE); - model.configure(year, mode, useGrossValue, useConsolidateRetired); + model.configure(year, mode, useGrossValue, useConsolidateRetired, excludenonCashEffective); model.addUpdateListener(() -> { preferences.setValue(KEY_YEAR, model.getStartYear()); preferences.setValue(KEY_MODE, model.getMode().name()); preferences.setValue(KEY_USE_GROSS_VALUE, model.usesGrossValue()); preferences.setValue(KEY_USE_CONSOLIDATE_RETIRED, model.usesConsolidateRetired()); + preferences.setValue(KEY_EXCLUDE_NON_CASH_EFFECTIVE, model.excludesnonCashEffective()); }); } @@ -176,6 +180,12 @@ protected void addButtons(ToolBarManager toolBar) action.setChecked(model.usesConsolidateRetired()); manager.add(action); + // cash effectiveness + action = new SimpleAction(Messages.LabelnonCashEffective, + a -> model.setexcludenonCashEffective(!model.excludesnonCashEffective())); + action.setChecked(model.excludesnonCashEffective()); + manager.add(action); + PaymentsTab tab = (PaymentsTab) folder.getSelection().getData(); if (tab != null) { diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/payments/PaymentsViewModel.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/payments/PaymentsViewModel.java index e62f4d75af..2e0d34aac5 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/payments/PaymentsViewModel.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/payments/PaymentsViewModel.java @@ -146,6 +146,7 @@ public T adapt(Class type) private Mode mode = Mode.ALL; private boolean useGrossValue = true; + private boolean excludenonCashEffective = true; private boolean useConsolidateRetired = true; public PaymentsViewModel(AbstractFinanceView view, IPreferenceStore preferences, CurrencyConverter converter, @@ -168,12 +169,14 @@ public PaymentsViewModel(AbstractFinanceView view, IPreferenceStore preferences, this.clientFilter.getSelectedItem().getUUIDs())); } - public void configure(int startYear, Mode mode, boolean useGrossValue, boolean useConsolidateRetired) + public void configure(int startYear, Mode mode, boolean useGrossValue, boolean useConsolidateRetired, + boolean excludenonCashEffective) { this.startYear = startYear; this.mode = mode; this.useGrossValue = useGrossValue; this.useConsolidateRetired = useConsolidateRetired; + this.excludenonCashEffective = excludenonCashEffective; recalculate(); } @@ -235,6 +238,17 @@ public void setUseGrossValue(boolean useGrossValue) recalculate(); } + public boolean excludesnonCashEffective() + { + return excludenonCashEffective; + } + + public void setexcludenonCashEffective(boolean excludenonCashEffective) + { + this.excludenonCashEffective = excludenonCashEffective; + recalculate(); + } + public boolean usesConsolidateRetired() { return useConsolidateRetired; @@ -399,6 +413,9 @@ private void calculate() if (!checkIsInInterval.test(transaction)) continue; + if (excludenonCashEffective && transaction.getnonCashEffective()) + continue; + long value = 0; switch (mode) { diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/model/Transaction.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/model/Transaction.java index 1bbdc59d6d..5c18fc8dac 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/model/Transaction.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/model/Transaction.java @@ -207,6 +207,7 @@ public int compare(Transaction t1, Transaction t2) private long shares; private String note; private String source; + private boolean nonCashEffective; private List units; @@ -240,6 +241,19 @@ public Transaction(LocalDateTime date, String currencyCode, long amount, Securit this.note = note; } + public Transaction(LocalDateTime date, String currencyCode, long amount, Security security, long shares, + String note, Boolean nonCashEffective) + { + this(); + this.date = date; + this.currencyCode = currencyCode; + this.amount = amount; + this.security = security; + this.shares = shares; + this.note = note; + this.nonCashEffective = nonCashEffective; + } + public String getUUID() { return uuid; @@ -357,6 +371,17 @@ public void setSource(String source) this.updatedAt = Instant.now(); } + public boolean getnonCashEffective() + { + return nonCashEffective; + } + + public void setnonCashEffective(Boolean nonCashEffective) + { + this.nonCashEffective = nonCashEffective; + this.updatedAt = Instant.now(); + } + public Instant getUpdatedAt() { return updatedAt;