Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added widget for the dashboard and columns for the information of dividends #3927

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
package name.abuchen.portfolio.ui.views.dashboard;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import name.abuchen.portfolio.model.Security;
import name.abuchen.portfolio.model.SecurityEvent;
import name.abuchen.portfolio.model.SecurityEvent.DividendEvent;
import name.abuchen.portfolio.money.Money;
import name.abuchen.portfolio.ui.Messages;
import name.abuchen.portfolio.ui.views.dashboard.DividendListWidget.DateEndRange;
import name.abuchen.portfolio.ui.views.dashboard.DividendListWidget.DateStartRange;
import name.abuchen.portfolio.ui.views.dashboard.DividendListWidget.DateType;
import name.abuchen.portfolio.ui.views.dashboard.DividendListWidget.DividendItem;

public class DividendListWidgetTest
kimmerin marked this conversation as resolved.
Show resolved Hide resolved
{
private static Locale defaultLocale;

private Security sec1;
private Security sec2;
private DividendEvent de1_1;
private DividendEvent de1_2;
private SecurityEvent de1_3;
private DividendEvent de1_4;
private DividendEvent de2_1;
private DividendEvent de2_2;
private SecurityEvent de2_3;
private DividendEvent de2_4;

@BeforeClass
public static void setupLocale()
{
defaultLocale = Locale.getDefault();
Locale.setDefault(Locale.GERMANY);
}

@AfterClass
public static void resetLocale()
{
Locale.setDefault(defaultLocale);
}

@Before
public void setupTestData()
{
sec1 = new Security("Security 1", "EUR");
de1_1 = new DividendEvent(LocalDate.of(2024, 4, 9), LocalDate.of(2024, 4, 12), Money.of("EUR", 55), "source");
de1_2 = new DividendEvent(LocalDate.of(2024, 7, 14), LocalDate.of(2024, 8, 11), Money.of("EUR", 22), "source");
de1_3 = new SecurityEvent(LocalDate.of(2024, 2, 3), SecurityEvent.Type.NOTE, "some note");
de1_4 = new DividendEvent(LocalDate.of(2024, 2, 4), LocalDate.of(2024, 3, 5), Money.of("EUR", 33), "source");
sec1.addEvent(de1_1);
sec1.addEvent(de1_2);
sec1.addEvent(de1_3);
sec1.addEvent(de1_4);

sec2 = new Security("Security 2", "EUR");
de2_1 = new DividendEvent(LocalDate.of(2024, 4, 5), LocalDate.of(2024, 4, 9), Money.of("USD", 55), "source");
de2_2 = new DividendEvent(LocalDate.of(2024, 7, 14), LocalDate.of(2024, 7, 15), Money.of("USD", 122), "source");
de2_3 = new SecurityEvent(LocalDate.of(2024, 8, 25), SecurityEvent.Type.STOCK_SPLIT, "some stock split");
de2_4 = new DividendEvent(LocalDate.of(2024, 2, 5), LocalDate.of(2024, 3, 5), Money.of("USD", 1333), "source");
sec2.addEvent(de2_1);
sec2.addEvent(de2_2);
sec2.addEvent(de2_3);
sec2.addEvent(de2_4);

}

@Test
public void testDividendItemInstantiation()
{
DividendItem di;

di = new DividendItem(DateType.EX_DIVIDEND_DATE, sec1, de1_1);
assertThat(di.getSecurity(), is(sec1));
assertThat(di.type, is(DateType.EX_DIVIDEND_DATE));
assertThat(di.div, is(de1_1));
}

@Test
public void testDateStartRange()
{
assertEquals("FROM_TODAY,FROM_ONE_WEEK,FROM_ONE_MONTH,FROM_YTD", //
Arrays.stream(DateStartRange.values())
.map(DateStartRange::name).collect(Collectors.joining(",")));

LocalDate now = LocalDate.of(2024, 4, 8);
assertThat(DateStartRange.FROM_TODAY.getDate(now), is(now));
assertThat(DateStartRange.FROM_ONE_WEEK.getDate(now), is(LocalDate.of(2024, 4, 1)));
assertThat(DateStartRange.FROM_ONE_MONTH.getDate(now), is(LocalDate.of(2024, 3, 8)));
assertThat(DateStartRange.FROM_YTD.getDate(now), is(LocalDate.of(2024, 1, 1)));
}

@Test
public void testDateEndRange()
{
assertEquals("UNTIL_EOY,UNTIL_ONE_MONTH,UNTIL_ONE_WEEK,UNTIL_TODAY", //
Arrays.stream(DateEndRange.values()).map(DateEndRange::name).collect(Collectors.joining(",")));

LocalDate now = LocalDate.of(2024, 4, 8);
assertThat(DateEndRange.UNTIL_TODAY.getDate(now), is(now));
assertThat(DateEndRange.UNTIL_ONE_WEEK.getDate(now), is(LocalDate.of(2024, 4, 15)));
assertThat(DateEndRange.UNTIL_ONE_MONTH.getDate(now), is(LocalDate.of(2024, 5, 8)));
assertThat(DateEndRange.UNTIL_EOY.getDate(now), is(LocalDate.of(2024, 12, 31)));
}

@Test
public void testDateType()
{
assertEquals("ALL_DATES,EX_DIVIDEND_DATE,PAYMENT_DATE", //
Arrays.stream(DateType.values()).map(DateType::name).collect(Collectors.joining(",")));

assertThat(DateType.ALL_DATES.getDate(de1_1), nullValue());
assertThat(DateType.EX_DIVIDEND_DATE.getDate(de1_1), is(de1_1.getDate()));
assertThat(DateType.PAYMENT_DATE.getDate(de1_1), is(de1_1.getPaymentDate()));
}

@Test
public void testGetUpdateTask() throws Exception
{
LocalDate testnow = LocalDate.now();
@SuppressWarnings("deprecation")
AbstractSecurityListWidget<DividendItem> widget = new DividendListWidget()
{
@Override
public Supplier<List<DividendItem>> getUpdateTask(LocalDate now)
{
assertThat(now, is(testnow));
return null;
}
};
assertThat(widget.getUpdateTask(), nullValue());
}

@Test
public void testGetUpdateTaskNow()
{
List<DividendItem> list;
AtomicReference<DateStartRange> dateStart = new AtomicReference<>(DateStartRange.FROM_TODAY);
AtomicReference<DateEndRange> dateEnd = new AtomicReference<>(DateEndRange.UNTIL_TODAY);
AtomicReference<DateType> dateType = new AtomicReference<>(DateType.ALL_DATES);

@SuppressWarnings("deprecation")
DividendListWidget widget = new DividendListWidget()
{
@Override
DateStartRange getStartRangeValue()
{
return dateStart.get();
}

@Override
DateEndRange getEndRangeValue()
{
return dateEnd.get();
}

@Override
DateType getDateTypeValue()
{
return dateType.get();
}

@Override
List<Security> getSecurities()
{
return Arrays.asList(sec1, sec2);
}
};

LocalDate now = LocalDate.of(2024, 4, 9);
list = widget.getUpdateTask(now).get();
assertEquals("2024-04-09 Security 1 EUR 0,55 " + Messages.ColumnExDate + "\r\n" //
+ "Security 2 USD 0,55 " + Messages.ColumnPaymentDate, getListAsString(list));

now = LocalDate.of(2024, 4, 8);
list = widget.getUpdateTask(now).get();
assertEquals("", getListAsString(list));

dateStart.set(DateStartRange.FROM_YTD);
list = widget.getUpdateTask(now).get();
assertEquals("2024-02-04 Security 1 EUR 0,33 " + Messages.ColumnExDate + "\r\n" //
+ "2024-02-05 Security 2 USD 13,33 " + Messages.ColumnExDate + "\r\n" //
+ "2024-03-05 Security 1 EUR 0,33 " + Messages.ColumnPaymentDate + "\r\n" //
+ "Security 2 USD 13,33 " + Messages.ColumnPaymentDate + "\r\n" //
+ "2024-04-05 Security 2 USD 0,55 " + Messages.ColumnExDate, getListAsString(list));

dateEnd.set(DateEndRange.UNTIL_EOY);
list = widget.getUpdateTask(now).get();
assertEquals("2024-02-04 Security 1 EUR 0,33 " + Messages.ColumnExDate + "\r\n" //
+ "2024-02-05 Security 2 USD 13,33 " + Messages.ColumnExDate + "\r\n" //
+ "2024-03-05 Security 1 EUR 0,33 " + Messages.ColumnPaymentDate + "\r\n" //
+ "Security 2 USD 13,33 " + Messages.ColumnPaymentDate + "\r\n" //
+ "2024-04-05 Security 2 USD 0,55 " + Messages.ColumnExDate + "\r\n" //
+ "2024-04-09 Security 1 EUR 0,55 " + Messages.ColumnExDate + "\r\n" //
+ "Security 2 USD 0,55 " + Messages.ColumnPaymentDate + "\r\n" //
+ "2024-04-12 Security 1 EUR 0,55 " + Messages.ColumnPaymentDate + "\r\n" //
+ "2024-07-14 Security 1 EUR 0,22 " + Messages.ColumnExDate + "\r\n" //
+ "Security 2 USD 1,22 " + Messages.ColumnExDate + "\r\n" //
+ "2024-07-15 Security 2 USD 1,22 " + Messages.ColumnPaymentDate + "\r\n" //
+ "2024-08-11 Security 1 EUR 0,22 " + Messages.ColumnPaymentDate, getListAsString(list));

dateType.set(DateType.EX_DIVIDEND_DATE);
list = widget.getUpdateTask(now).get();
assertEquals("2024-02-04 Security 1 EUR 0,33 " + Messages.ColumnExDate + "\r\n" //
+ "2024-02-05 Security 2 USD 13,33 " + Messages.ColumnExDate + "\r\n" //
+ "2024-04-05 Security 2 USD 0,55 " + Messages.ColumnExDate + "\r\n" //
+ "2024-04-09 Security 1 EUR 0,55 " + Messages.ColumnExDate + "\r\n" //
+ "2024-07-14 Security 1 EUR 0,22 " + Messages.ColumnExDate + "\r\n" //
+ "Security 2 USD 1,22 " + Messages.ColumnExDate, getListAsString(list));

dateType.set(DateType.PAYMENT_DATE);
list = widget.getUpdateTask(now).get();
assertEquals("2024-03-05 Security 1 EUR 0,33 " + Messages.ColumnPaymentDate + "\r\n" //
+ "Security 2 USD 13,33 " + Messages.ColumnPaymentDate + "\r\n" //
+ "2024-04-09 Security 2 USD 0,55 " + Messages.ColumnPaymentDate + "\r\n" //
+ "2024-04-12 Security 1 EUR 0,55 " + Messages.ColumnPaymentDate + "\r\n" //
+ "2024-07-15 Security 2 USD 1,22 " + Messages.ColumnPaymentDate + "\r\n" //
+ "2024-08-11 Security 1 EUR 0,22 " + Messages.ColumnPaymentDate, getListAsString(list));
}

private String getListAsString(List<DividendItem> list)
{
return list.stream() //
.map(entry -> {
StringBuilder sb = new StringBuilder();
if (entry.hasNewDate)
{
sb.append(entry.type.getDate(entry.div).toString() + " ");
}
sb.append(entry.getSecurity().getName() + " ");
sb.append(entry.div.getAmount() + " ");
sb.append(entry.type);
return sb.toString();
}) //
.collect(Collectors.joining("\r\n"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1320,6 +1320,17 @@ public class Messages extends NLS
public static String OptionDateIsInTheFuture;
public static String OptionDateIsInThePast;
public static String YearlyPerformanceHeatmapToolTip;

public static String LabelEarningsDividendList;
public static String LabelEarningsDividendPeriodFrom;
public static String LabelEarningsDividendPeriodUntil;
public static String LabelEarningsDividendDateType;
public static String ColumnDividendsNextExDate;
public static String ColumnDividendsNextExDate_MenuLabel;
public static String ColumnDividendsNextPaymentDate;
public static String ColumnDividendsNextPaymentDate_MenuLabel;
public static String ColumnDividendsNextPaymentAmount;
public static String ColumnDividendsNextPaymentAmount_MenuLabel;
kimmerin marked this conversation as resolved.
Show resolved Hide resolved
static
{
// initialize resource bundle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2631,3 +2631,23 @@ 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.

LabelEarningsDividendList = Upcoming dividends

LabelEarningsDividendPeriodFrom = Period start

LabelEarningsDividendPeriodUntil = Period end

LabelEarningsDividendDateType = Type of date

ColumnDividendsNextExDate = Next Ex-Date

ColumnDividendsNextExDate_MenuLabel = Next Dividend Ex-Date

ColumnDividendsNextPaymentDate = Next Paym. Date

ColumnDividendsNextPaymentDate_MenuLabel = Next Dividend Payment Date

ColumnDividendsNextPaymentAmount = Next Paym. Amt.

ColumnDividendsNextPaymentAmount_MenuLabel = Next Divident Payment Amount
Original file line number Diff line number Diff line change
Expand Up @@ -2624,3 +2624,23 @@ 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.

LabelEarningsDividendList = \u00DCbersicht anstehender Dividenden

LabelEarningsDividendPeriodFrom = Zeitraum ab

LabelEarningsDividendPeriodUntil = Zeitraum bis

LabelEarningsDividendDateType = Datumsart

ColumnDividendsNextExDate = Div Ex. Tag

ColumnDividendsNextExDate_MenuLabel = Nächster Ex-Dividendentag

ColumnDividendsNextPaymentDate = Div. Zahltag

ColumnDividendsNextPaymentDate_MenuLabel = Nächster Dividenden Zahltag

ColumnDividendsNextPaymentAmount = Div. Betrag

ColumnDividendsNextPaymentAmount_MenuLabel = Nächster Dividendenbetrag
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import name.abuchen.portfolio.ui.util.viewers.ListEditingSupport;
import name.abuchen.portfolio.ui.util.viewers.ShowHideColumnHelper;
import name.abuchen.portfolio.ui.views.columns.AttributeColumn;
import name.abuchen.portfolio.ui.views.columns.DividendPaymentColumn;
import name.abuchen.portfolio.ui.views.columns.NameColumn;
import name.abuchen.portfolio.ui.views.columns.NameColumn.NameColumnLabelProvider;
import name.abuchen.portfolio.ui.views.columns.NoteColumn;
Expand Down Expand Up @@ -259,6 +260,7 @@ public String getText(Object element)
portfolioColumns.addColumn(column);

addAttributeColumns(portfolioColumns);
addDividendPaymentColumns(portfolioColumns);
kimmerin marked this conversation as resolved.
Show resolved Hide resolved

portfolioColumns.createColumns(true);

Expand All @@ -278,6 +280,14 @@ public String getText(Object element)
return container;
}

private void addDividendPaymentColumns(ShowHideColumnHelper support)
{
DividendPaymentColumn.createFor() //
.forEach(column -> {
support.addColumn(column);
});
}

private void addAttributeColumns(ShowHideColumnHelper support)
{
AttributeColumn.createFor(getClient(), Portfolio.class) //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
import name.abuchen.portfolio.ui.views.columns.AttributeColumn;
import name.abuchen.portfolio.ui.views.columns.DistanceFromAllTimeHighColumn;
import name.abuchen.portfolio.ui.views.columns.DistanceFromMovingAverageColumn;
import name.abuchen.portfolio.ui.views.columns.DividendPaymentColumn;
import name.abuchen.portfolio.ui.views.columns.IsinColumn;
import name.abuchen.portfolio.ui.views.columns.NoteColumn;
import name.abuchen.portfolio.ui.views.columns.SymbolColumn;
Expand Down Expand Up @@ -219,6 +220,7 @@ public SecuritiesTable(Composite parent, AbstractFinanceView view)
}

addAttributeColumns();
addDividendColumns();
addQuoteFeedColumns();
addDataQualityColumns();

Expand All @@ -240,6 +242,14 @@ public SecuritiesTable(Composite parent, AbstractFinanceView view)
hookContextMenu();
}

private void addDividendColumns()
{
DividendPaymentColumn.createFor() //
.forEach(column -> {
support.addColumn(column);
});
}

private void addMasterDataColumns()
{
Column column = new Column("0", Messages.ColumnName, SWT.LEFT, 400); //$NON-NLS-1$
Expand Down
Loading
Loading