package name.abuchen.portfolio.ui.views.dividends; import java.time.LocalDate; import java.time.Month; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Predicate; import org.eclipse.jface.preference.IPreferenceStore; import name.abuchen.portfolio.model.Account; import name.abuchen.portfolio.model.AccountTransaction; import name.abuchen.portfolio.model.Client; import name.abuchen.portfolio.model.InvestmentVehicle; import name.abuchen.portfolio.model.Transaction; import name.abuchen.portfolio.model.TransactionPair; import name.abuchen.portfolio.money.CurrencyConverter; import name.abuchen.portfolio.money.Money; import name.abuchen.portfolio.snapshot.ReportingPeriod; import name.abuchen.portfolio.ui.util.ClientFilterMenu; public class DividendsViewModel { @FunctionalInterface public interface UpdateListener { void onUpdate(); } public static class Line { private InvestmentVehicle vehicle; private long[] values; private long sum; public Line(InvestmentVehicle vehicle, int length) { this.vehicle = vehicle; this.values = new long[length]; } public InvestmentVehicle getVehicle() { return vehicle; } public long getValue(int index) { return values[index]; } public long getSum() { return sum; } } private List<UpdateListener> listeners = new ArrayList<>(); private final CurrencyConverter converter; private final Client client; private final ClientFilterMenu clientFilter; private int startYear; private int noOfmonths; private List<Line> lines; private Line sum; private List<TransactionPair<AccountTransaction>> transactions; private boolean useGrossValue = true; public DividendsViewModel(IPreferenceStore preferences, CurrencyConverter converter, Client client) { this.converter = converter; this.client = client; this.clientFilter = new ClientFilterMenu(client, preferences, filter -> recalculate()); } public ClientFilterMenu getClientFilterMenu() { return clientFilter; } public int getStartYear() { return startYear; } public int getNoOfMonths() { return noOfmonths; } public List<Line> getLines() { return lines; } public Line getSum() { return sum; } public boolean usesGrossValue() { return useGrossValue; } public void setUseGrossValue(boolean useGrossValue) { this.useGrossValue = useGrossValue; calculate(); fireUpdateChange(); } /** * Returns all lines including the sum line */ public List<Line> getAllLines() { List<Line> answer = new ArrayList<>(); answer.addAll(lines); answer.add(sum); return answer; } public List<TransactionPair<AccountTransaction>> getTransactions() { return transactions; } public void updateWith(int year) { this.startYear = year; calculate(); fireUpdateChange(); } public void recalculate() { calculate(); fireUpdateChange(); } private void calculate() { // determine the number of full months within period LocalDate now = LocalDate.now(); if (startYear > now.getYear()) throw new IllegalArgumentException(); this.noOfmonths = (now.getYear() - startYear) * 12 + now.getMonthValue(); Predicate<Transaction> predicate = new ReportingPeriod.FromXtoY(LocalDate.of(startYear - 1, Month.DECEMBER, 31), now).containsTransaction(); Map<InvestmentVehicle, Line> vehicle2line = new HashMap<>(); this.sum = new Line(null, this.noOfmonths); this.transactions = new ArrayList<>(); Client filteredClient = clientFilter.getSelectedFilter().filter(client); for (Account account : filteredClient.getAccounts()) { for (AccountTransaction t : account.getTransactions()) { if (t.getType() != AccountTransaction.Type.DIVIDENDS) continue; if (!predicate.test(t)) continue; transactions.add(new TransactionPair<>(account, t)); Money dividendValue = useGrossValue ? t.getGrossValue() : t.getMonetaryAmount(); long value = dividendValue.with(converter.at(t.getDate())).getAmount(); int index = (t.getDate().getYear() - startYear) * 12 + t.getDate().getMonthValue() - 1; Line line = vehicle2line.computeIfAbsent(t.getSecurity(), s -> new Line(s, noOfmonths)); line.values[index] += value; line.sum += value; sum.values[index] += value; sum.sum += value; } } this.lines = new ArrayList<>(vehicle2line.values()); } public void addUpdateListener(UpdateListener listener) { this.listeners.add(listener); } protected void fireUpdateChange() { this.listeners.stream().forEach(UpdateListener::onUpdate); } }