#29 Projected cash flow
This commit is contained in:
@@ -150,27 +150,25 @@ public class ReportController {
|
|||||||
new ParameterizedTypeReference<Iterable<RecurringTransactionDueInRangeResponseDto>>() {
|
new ParameterizedTypeReference<Iterable<RecurringTransactionDueInRangeResponseDto>>() {
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<RecurringTransaction> incomeTransactions = new ArrayList<>();
|
final ConfigProjectedCashFlowForPeriodTransactionsForm formTransactions =
|
||||||
final List<RecurringTransaction> expenseTransactions = new ArrayList<>();
|
new ConfigProjectedCashFlowForPeriodTransactionsForm(
|
||||||
|
ControllerUtils.parseDate(this.financerConfig, form.getFromDate()),
|
||||||
|
ControllerUtils.parseDate(this.financerConfig, form.getToDate()));
|
||||||
final EnumMap<AccountType, List<AccountType>> bookingRules = new EnumMap<>(AccountType.class);
|
final EnumMap<AccountType, List<AccountType>> bookingRules = new EnumMap<>(AccountType.class);
|
||||||
|
|
||||||
bookingRules.put(BANK, Arrays.asList(BANK, EXPENSE, LIABILITY));
|
bookingRules.put(BANK, Arrays.asList(BANK, EXPENSE, LIABILITY));
|
||||||
bookingRules.put(CASH, Arrays.asList(EXPENSE, LIABILITY));
|
bookingRules.put(CASH, Arrays.asList(EXPENSE, LIABILITY));
|
||||||
|
|
||||||
IterableUtils.toList(trxs).stream()
|
IterableUtils.toList(trxs).stream()
|
||||||
.flatMap(dto -> IterableUtils.toList(dto.getRecurringTransactions()).stream()).forEach(rt -> {
|
.forEach(dto -> IterableUtils.toList(dto.getRecurringTransactions()).stream().forEach(rt -> {
|
||||||
if (AccountType.INCOME == rt.getFromAccount().getType()) {
|
if (AccountType.INCOME == rt.getFromAccount().getType()) {
|
||||||
incomeTransactions.add(rt);
|
formTransactions.addIncomeTransaction(dto.getDate(), rt);
|
||||||
} else if (bookingRules.getOrDefault(rt.getFromAccount().getType(), Collections.EMPTY_LIST)
|
} else if (bookingRules.getOrDefault(rt.getFromAccount().getType(), Collections.EMPTY_LIST)
|
||||||
.contains(rt.getToAccount().getType())) {
|
.contains(rt.getToAccount().getType())) {
|
||||||
expenseTransactions.add(rt);
|
formTransactions.addExpenseTransaction(dto.getDate(), rt);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
));
|
||||||
ConfigProjectedCashFlowForPeriodTransactionsForm formTransactions =
|
|
||||||
new ConfigProjectedCashFlowForPeriodTransactionsForm(incomeTransactions, expenseTransactions,
|
|
||||||
ControllerUtils.parseDate(this.financerConfig, form.getFromDate()),
|
|
||||||
ControllerUtils.parseDate(this.financerConfig, form.getToDate()));
|
|
||||||
|
|
||||||
final ResponseEntity<Iterable<Account>> response = new GetAllAccountsTemplate().exchange(this.financerConfig);
|
final ResponseEntity<Iterable<Account>> response = new GetAllAccountsTemplate().exchange(this.financerConfig);
|
||||||
|
|
||||||
@@ -188,6 +186,8 @@ public class ReportController {
|
|||||||
|
|
||||||
return "report/configureProjectedCashFlowForPeriodTransactions";
|
return "report/configureProjectedCashFlowForPeriodTransactions";
|
||||||
} catch (FinancerRestException e) {
|
} catch (FinancerRestException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
model.addAttribute("errorMessage", e.getResponseReason().name());
|
model.addAttribute("errorMessage", e.getResponseReason().name());
|
||||||
model.addAttribute("form", form);
|
model.addAttribute("form", form);
|
||||||
ControllerUtils.addVersionAttribute(model, this.financerConfig);
|
ControllerUtils.addVersionAttribute(model, this.financerConfig);
|
||||||
|
|||||||
@@ -83,8 +83,38 @@ public class ConfigProjectedCashFlowForPeriodTransactionsForm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<RecurringTransaction> incomeTransactions;
|
public static class RecurringTransactionContainer {
|
||||||
private List<RecurringTransaction> expenseTransactions;
|
private RecurringTransaction recurringTransaction;
|
||||||
|
private LocalDate date;
|
||||||
|
|
||||||
|
public RecurringTransactionContainer() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecurringTransactionContainer(RecurringTransaction recurringTransaction, LocalDate date) {
|
||||||
|
this.date = date;
|
||||||
|
this.recurringTransaction = recurringTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecurringTransaction getRecurringTransaction() {
|
||||||
|
return recurringTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecurringTransaction(RecurringTransaction recurringTransaction) {
|
||||||
|
this.recurringTransaction = recurringTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getDate() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDate(LocalDate date) {
|
||||||
|
this.date = date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<RecurringTransactionContainer> incomeTransactions;
|
||||||
|
private List<RecurringTransactionContainer> expenseTransactions;
|
||||||
private List<AccountGroupAccountContainer> accountGroupAccountContainers;
|
private List<AccountGroupAccountContainer> accountGroupAccountContainers;
|
||||||
private long incomeSum;
|
private long incomeSum;
|
||||||
private long expenseSum;
|
private long expenseSum;
|
||||||
@@ -99,23 +129,21 @@ public class ConfigProjectedCashFlowForPeriodTransactionsForm {
|
|||||||
this.accountGroupAccountContainers = new ArrayList<>();
|
this.accountGroupAccountContainers = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigProjectedCashFlowForPeriodTransactionsForm(List<RecurringTransaction> incomeTransactions,
|
public ConfigProjectedCashFlowForPeriodTransactionsForm(LocalDate fromDate, LocalDate toDate
|
||||||
List<RecurringTransaction> expenseTransactions,
|
|
||||||
LocalDate fromDate, LocalDate toDate
|
|
||||||
) {
|
) {
|
||||||
this.incomeTransactions = incomeTransactions;
|
this.incomeTransactions = new ArrayList<>();
|
||||||
this.expenseTransactions = expenseTransactions;
|
this.expenseTransactions = new ArrayList<>();
|
||||||
this.accountGroupAccountContainers = new ArrayList<>();
|
this.accountGroupAccountContainers = new ArrayList<>();
|
||||||
this.fromDate = fromDate;
|
this.fromDate = fromDate;
|
||||||
this.toDate = toDate;
|
this.toDate = toDate;
|
||||||
|
|
||||||
Collections.sort(this.incomeTransactions, Comparator.comparing(RecurringTransaction::getAmount));
|
|
||||||
Collections.sort(this.expenseTransactions, Comparator.comparing(RecurringTransaction::getAmount));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refresh() {
|
public void refresh() {
|
||||||
this.incomeSum = incomeTransactions.stream().mapToLong(RecurringTransaction::getAmount).sum();
|
Collections.sort(this.incomeTransactions, Comparator.comparing(con -> con.getRecurringTransaction().getAmount()));
|
||||||
this.expenseSum = expenseTransactions.stream().mapToLong(RecurringTransaction::getAmount).sum();
|
Collections.sort(this.expenseTransactions, Comparator.comparing(con -> con.getRecurringTransaction().getAmount()));
|
||||||
|
|
||||||
|
this.incomeSum = incomeTransactions.stream().mapToLong(con -> con.getRecurringTransaction().getAmount()).sum();
|
||||||
|
this.expenseSum = expenseTransactions.stream().mapToLong(con -> con.getRecurringTransaction().getAmount()).sum();
|
||||||
this.cashFlow = this.incomeSum - this.expenseSum;
|
this.cashFlow = this.incomeSum - this.expenseSum;
|
||||||
|
|
||||||
this.accountSum = this.accountGroupAccountContainers.stream().map(AccountGroupAccountContainer::getAccounts)
|
this.accountSum = this.accountGroupAccountContainers.stream().map(AccountGroupAccountContainer::getAccounts)
|
||||||
@@ -125,19 +153,19 @@ public class ConfigProjectedCashFlowForPeriodTransactionsForm {
|
|||||||
this.cashFlow = this.cashFlow - this.accountSum;
|
this.cashFlow = this.cashFlow - this.accountSum;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<RecurringTransaction> getIncomeTransactions() {
|
public List<RecurringTransactionContainer> getIncomeTransactions() {
|
||||||
return incomeTransactions;
|
return incomeTransactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIncomeTransactions(List<RecurringTransaction> incomeTransactions) {
|
public void setIncomeTransactions(List<RecurringTransactionContainer> incomeTransactions) {
|
||||||
this.incomeTransactions = incomeTransactions;
|
this.incomeTransactions = incomeTransactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<RecurringTransaction> getExpenseTransactions() {
|
public List<RecurringTransactionContainer> getExpenseTransactions() {
|
||||||
return expenseTransactions;
|
return expenseTransactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExpenseTransactions(List<RecurringTransaction> expenseTransactions) {
|
public void setExpenseTransactions(List<RecurringTransactionContainer> expenseTransactions) {
|
||||||
this.expenseTransactions = expenseTransactions;
|
this.expenseTransactions = expenseTransactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +185,14 @@ public class ConfigProjectedCashFlowForPeriodTransactionsForm {
|
|||||||
.collect(Collectors.toList())));
|
.collect(Collectors.toList())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addIncomeTransaction(LocalDate date, RecurringTransaction recurringTransaction) {
|
||||||
|
this.incomeTransactions.add(new RecurringTransactionContainer(recurringTransaction, date));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addExpenseTransaction(LocalDate date, RecurringTransaction recurringTransaction) {
|
||||||
|
this.expenseTransactions.add(new RecurringTransactionContainer(recurringTransaction, date));
|
||||||
|
}
|
||||||
|
|
||||||
public long getIncomeSum() {
|
public long getIncomeSum() {
|
||||||
return incomeSum;
|
return incomeSum;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,10 @@
|
|||||||
<body>
|
<body>
|
||||||
<h1 th:text="#{financer.heading.report-config-projected-cashflow-for-period-transactions}" class="noprint"/>
|
<h1 th:text="#{financer.heading.report-config-projected-cashflow-for-period-transactions}" class="noprint"/>
|
||||||
<h1 th:text="#{financer.heading.report-config-projected-cashflow-for-period-transactions-print}" class="print"/>
|
<h1 th:text="#{financer.heading.report-config-projected-cashflow-for-period-transactions-print}" class="print"/>
|
||||||
<span th:text="${#temporals.format(form.fromDate) + ' - ' + #temporals.format(form.toDate)}" class="print print-padding" />
|
<span th:text="${#temporals.format(form.fromDate) + ' - ' + #temporals.format(form.toDate)}"
|
||||||
<span class="errorMessage noprint" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}" />
|
class="print print-padding"/>
|
||||||
|
<span class="errorMessage noprint" th:if="${errorMessage != null}"
|
||||||
|
th:text="#{'financer.error-message.' + ${errorMessage}}"/>
|
||||||
<a th:href="@{/accountOverview}" th:text="#{financer.cancel-back-to-overview}" class="noprint"/>
|
<a th:href="@{/accountOverview}" th:text="#{financer.cancel-back-to-overview}" class="noprint"/>
|
||||||
<form id="report-config-projected-cashflow-for-period-transactions-form" action="#" th:object="${form}" method="post">
|
<form id="report-config-projected-cashflow-for-period-transactions-form" action="#" th:object="${form}" method="post">
|
||||||
<table id="report-config-projected-cashflow-for-period-transactions-table">
|
<table id="report-config-projected-cashflow-for-period-transactions-table">
|
||||||
@@ -24,14 +26,17 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr th:each="entry, i : ${form.incomeTransactions}">
|
<tr th:each="entry, i : ${form.incomeTransactions}">
|
||||||
<td class="projected-cashflow-transaction">
|
<td class="projected-cashflow-transaction">
|
||||||
<span th:text="*{incomeTransactions[__${i.index}__].description}" />
|
<span th:text="*{incomeTransactions[__${i.index}__].recurringTransaction.description + ' (' + #temporals.format(incomeTransactions[__${i.index}__].date) + ')'}"/>
|
||||||
<input type="hidden" th:field="*{incomeTransactions[__${i.index}__].description}" class="display-none" />
|
<input type="hidden" th:field="*{incomeTransactions[__${i.index}__].recurringTransaction.description}"
|
||||||
|
class="display-none"/>
|
||||||
|
<input type="hidden" th:field="*{incomeTransactions[__${i.index}__].date}"
|
||||||
|
class="display-none"/>
|
||||||
</td>
|
</td>
|
||||||
<td class="report-config-projected-cashflow-for-period-transactions-table-amount noprint">
|
<td class="report-config-projected-cashflow-for-period-transactions-table-amount noprint">
|
||||||
<input type="text" th:field="*{incomeTransactions[__${i.index}__].amount}" />
|
<input type="text" th:field="*{incomeTransactions[__${i.index}__].recurringTransaction.amount}"/>
|
||||||
</td>
|
</td>
|
||||||
<td class="report-config-projected-cashflow-for-period-transactions-table-amount print">
|
<td class="report-config-projected-cashflow-for-period-transactions-table-amount print">
|
||||||
<span th:utext="${#numbers.formatDecimal(form.incomeTransactions[__${i.index}__].amount/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
|
<span th:utext="${#numbers.formatDecimal(form.incomeTransactions[__${i.index}__].recurringTransaction.amount/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="projected-cashflow-sum">
|
<tr class="projected-cashflow-sum">
|
||||||
@@ -41,14 +46,17 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr th:each="entry, i : ${form.expenseTransactions}">
|
<tr th:each="entry, i : ${form.expenseTransactions}">
|
||||||
<td class="projected-cashflow-transaction">
|
<td class="projected-cashflow-transaction">
|
||||||
<span th:text="*{expenseTransactions[__${i.index}__].description}" />
|
<span th:text="*{expenseTransactions[__${i.index}__].description + ' (' + #temporals.format(expenseTransactions[__${i.index}__].date) + ')'}"/>
|
||||||
<input type="hidden" th:field="*{expenseTransactions[__${i.index}__].description}" class="display-none" />
|
<input type="hidden" th:field="*{expenseTransactions[__${i.index}__].recurringTransaction.description}"
|
||||||
|
class="display-none"/>
|
||||||
|
<input type="hidden" th:field="*{expenseTransactions[__${i.index}__].date}"
|
||||||
|
class="display-none"/>
|
||||||
</td>
|
</td>
|
||||||
<td class="report-config-projected-cashflow-for-period-transactions-table-amount noprint">
|
<td class="report-config-projected-cashflow-for-period-transactions-table-amount noprint">
|
||||||
<input type="text" th:field="*{expenseTransactions[__${i.index}__].amount}" />
|
<input type="text" th:field="*{expenseTransactions[__${i.index}__].recurringTransaction.amount}"/>
|
||||||
</td>
|
</td>
|
||||||
<td class="report-config-projected-cashflow-for-period-transactions-table-amount print">
|
<td class="report-config-projected-cashflow-for-period-transactions-table-amount print">
|
||||||
<span th:utext="${#numbers.formatDecimal(form.expenseTransactions[__${i.index}__].amount/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
|
<span th:utext="${#numbers.formatDecimal(form.expenseTransactions[__${i.index}__].recurringTransaction.amount/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="projected-cashflow-sum">
|
<tr class="projected-cashflow-sum">
|
||||||
@@ -56,13 +64,14 @@
|
|||||||
<td th:utext="${#numbers.formatDecimal((form.accountSum/100D)*-1, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"
|
<td th:utext="${#numbers.formatDecimal((form.accountSum/100D)*-1, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"
|
||||||
class="report-config-projected-cashflow-for-period-transactions-table-amount"/>
|
class="report-config-projected-cashflow-for-period-transactions-table-amount"/>
|
||||||
<td th:text="#{financer.account-overview.table-header.average-spending-period}"
|
<td th:text="#{financer.account-overview.table-header.average-spending-period}"
|
||||||
class="report-config-projected-cashflow-for-period-transactions-table-average-spending noprint"/>
|
class="report-config-projected-cashflow-for-period-transactions-table-average-spending noprint"/>
|
||||||
</tr>
|
</tr>
|
||||||
<th:block th:each="entry, i : ${form.accountGroupAccountContainers}">
|
<th:block th:each="entry, i : ${form.accountGroupAccountContainers}">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="projected-cashflow-transaction">
|
<td class="projected-cashflow-transaction">
|
||||||
<span th:text="*{accountGroupAccountContainers[__${i.index}__].accountGroup}" />
|
<span th:text="*{accountGroupAccountContainers[__${i.index}__].accountGroup}"/>
|
||||||
<input type="hidden" th:field="*{accountGroupAccountContainers[__${i.index}__].accountGroup}" class="display-none" />
|
<input type="hidden" th:field="*{accountGroupAccountContainers[__${i.index}__].accountGroup}"
|
||||||
|
class="display-none"/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span></span>
|
<span></span>
|
||||||
@@ -73,18 +82,23 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr th:each="listEntry, j : ${entry.accounts}">
|
<tr th:each="listEntry, j : ${entry.accounts}">
|
||||||
<td class="projected-cashflow-account">
|
<td class="projected-cashflow-account">
|
||||||
<span th:text="*{accountGroupAccountContainers[__${i.index}__].accounts[__${j.index}__].account.key}" />
|
<span th:text="*{accountGroupAccountContainers[__${i.index}__].accounts[__${j.index}__].account.key}"/>
|
||||||
<input type="hidden" th:field="*{accountGroupAccountContainers[__${i.index}__].accounts[__${j.index}__].account.key}" class="display-none" />
|
<input type="hidden"
|
||||||
|
th:field="*{accountGroupAccountContainers[__${i.index}__].accounts[__${j.index}__].account.key}"
|
||||||
|
class="display-none"/>
|
||||||
</td>
|
</td>
|
||||||
<td class="report-config-projected-cashflow-for-period-transactions-table-amount noprint">
|
<td class="report-config-projected-cashflow-for-period-transactions-table-amount noprint">
|
||||||
<input type="text" th:field="*{accountGroupAccountContainers[__${i.index}__].accounts[__${j.index}__].spending}" />
|
<input type="text"
|
||||||
|
th:field="*{accountGroupAccountContainers[__${i.index}__].accounts[__${j.index}__].spending}"/>
|
||||||
</td>
|
</td>
|
||||||
<td class="report-config-projected-cashflow-for-period-transactions-table-amount print">
|
<td class="report-config-projected-cashflow-for-period-transactions-table-amount print">
|
||||||
<span th:utext="${#numbers.formatDecimal(form.accountGroupAccountContainers[__${i.index}__].accounts[__${j.index}__].spending/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
|
<span th:utext="${#numbers.formatDecimal(form.accountGroupAccountContainers[__${i.index}__].accounts[__${j.index}__].spending/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
|
||||||
</td>
|
</td>
|
||||||
<td class="noprint report-config-projected-cashflow-for-period-transactions-table-average-spending">
|
<td class="noprint report-config-projected-cashflow-for-period-transactions-table-average-spending">
|
||||||
<span th:utext="${#numbers.formatDecimal(form.accountGroupAccountContainers[__${i.index}__].accounts[__${j.index}__].averageSpending/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
|
<span th:utext="${#numbers.formatDecimal(form.accountGroupAccountContainers[__${i.index}__].accounts[__${j.index}__].averageSpending/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
|
||||||
<input type="hidden" th:field="*{accountGroupAccountContainers[__${i.index}__].accounts[__${j.index}__].averageSpending}" class="display-none" />
|
<input type="hidden"
|
||||||
|
th:field="*{accountGroupAccountContainers[__${i.index}__].accounts[__${j.index}__].averageSpending}"
|
||||||
|
class="display-none"/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</th:block>
|
</th:block>
|
||||||
@@ -98,10 +112,12 @@
|
|||||||
<input type="hidden" th:field="*{fromDate}" class="display-none"/>
|
<input type="hidden" th:field="*{fromDate}" class="display-none"/>
|
||||||
<input type="hidden" th:field="*{toDate}" class="display-none"/>
|
<input type="hidden" th:field="*{toDate}" class="display-none"/>
|
||||||
<input type="submit" th:formaction="@{/configureProjectedCashFlowForPeriodTransactionsRefresh}"
|
<input type="submit" th:formaction="@{/configureProjectedCashFlowForPeriodTransactionsRefresh}"
|
||||||
th:value="#{financer.report-config-projected-cashflow-for-period-transactions.label.button.refresh}" class="noprint"/>
|
th:value="#{financer.report-config-projected-cashflow-for-period-transactions.label.button.refresh}"
|
||||||
|
class="noprint"/>
|
||||||
<input type="submit" onClick="window.print()"
|
<input type="submit" onClick="window.print()"
|
||||||
th:value="#{financer.report-config-projected-cashflow-for-period-transactions.label.button.create}" class="noprint"/>
|
th:value="#{financer.report-config-projected-cashflow-for-period-transactions.label.button.create}"
|
||||||
|
class="noprint"/>
|
||||||
</form>
|
</form>
|
||||||
<div th:replace="includes/footer :: footer" />
|
<div th:replace="includes/footer :: footer"/>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user