#29 Projected cash flow

This commit is contained in:
2022-05-17 17:09:44 +02:00
parent 3e60485bb4
commit e755e15de8
16 changed files with 702 additions and 30 deletions

View File

@@ -2,17 +2,32 @@ package de.financer.controller;
import de.financer.chart.ChartType; import de.financer.chart.ChartType;
import de.financer.config.FinancerConfig; import de.financer.config.FinancerConfig;
import de.financer.form.ConfigAccountExpenseForPeriodForm; import de.financer.decorator.AccountDecorator;
import de.financer.form.ConfigAccountGroupExpenseForPeriodForm; import de.financer.dto.RecurringTransactionDueInRangeResponseDto;
import de.financer.form.SelectChartForm; import de.financer.form.*;
import de.financer.model.Account;
import de.financer.model.AccountType;
import de.financer.model.RecurringTransaction;
import de.financer.report.ReportType;
import de.financer.template.FinancerRestTemplate;
import de.financer.template.GetAllAccountsTemplate;
import de.financer.template.exception.FinancerRestException;
import de.financer.util.ControllerUtils; import de.financer.util.ControllerUtils;
import org.apache.commons.collections4.IterableUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.NoSuchElementException; import java.util.*;
import java.util.stream.Collectors;
import static de.financer.model.AccountType.*;
import static de.financer.model.AccountType.LIABILITY;
@Controller @Controller
public class ReportController { public class ReportController {
@@ -36,8 +51,7 @@ public class ReportController {
try { try {
selectedChartType = ChartType.getByValue(form.getChartType()); selectedChartType = ChartType.getByValue(form.getChartType());
} } catch (NoSuchElementException nsee) {
catch (NoSuchElementException nsee) {
model.addAttribute("form", new SelectChartForm()); model.addAttribute("form", new SelectChartForm());
model.addAttribute("errorMessage", "UNKNOWN_CHART_TYPE"); model.addAttribute("errorMessage", "UNKNOWN_CHART_TYPE");
model.addAttribute("availableCharts", ChartType.valueList(true)); model.addAttribute("availableCharts", ChartType.valueList(true));
@@ -48,7 +62,7 @@ public class ReportController {
return "report/selectChart"; return "report/selectChart";
} }
switch(selectedChartType) { switch (selectedChartType) {
case ACCOUNT_GROUP_EXPENSES_CURRENT_PERIOD: case ACCOUNT_GROUP_EXPENSES_CURRENT_PERIOD:
// Special case: this chart does not require parameters, so we can // Special case: this chart does not require parameters, so we can
// directly redirect to the actual chart instead of the config page // directly redirect to the actual chart instead of the config page
@@ -80,4 +94,126 @@ public class ReportController {
throw new IllegalStateException("Unexpected value: " + selectedChartType); throw new IllegalStateException("Unexpected value: " + selectedChartType);
} }
} }
@GetMapping("/selectReport")
public String selectReport(Model model) {
model.addAttribute("form", new SelectReportForm());
model.addAttribute("availableReports", ReportType.valueList());
ControllerUtils.addVersionAttribute(model, this.financerConfig);
ControllerUtils.addCurrencySymbol(model, this.financerConfig);
ControllerUtils.addDarkMode(model, this.financerConfig);
return "report/selectReport";
}
@PostMapping("/configureReport")
public String configureReport(SelectReportForm form, Model model) {
final ReportType selectedReportType;
try {
selectedReportType = ReportType.getByValue(form.getReportType());
} catch (NoSuchElementException nsee) {
model.addAttribute("form", new SelectReportForm());
model.addAttribute("errorMessage", "UNKNOWN_REPORT_TYPE");
model.addAttribute("availableReports", ReportType.valueList());
ControllerUtils.addVersionAttribute(model, this.financerConfig);
ControllerUtils.addCurrencySymbol(model, this.financerConfig);
ControllerUtils.addDarkMode(model, this.financerConfig);
return "report/selectReport";
}
switch (selectedReportType) {
case PROJECTED_CASH_FLOW:
model.addAttribute("form", new ConfigProjectedCashFlowForPeriodForm());
ControllerUtils.addVersionAttribute(model, this.financerConfig);
ControllerUtils.addCurrencySymbol(model, this.financerConfig);
ControllerUtils.addDarkMode(model, this.financerConfig);
return "report/configureProjectedCashFlowForPeriod";
default:
// Cannot happen
throw new IllegalStateException("Unexpected value: " + selectedReportType);
}
}
@PostMapping("/configureProjectedCashFlowForPeriodTransactions")
public String configureProjectedCashFlowForPeriodTransactions(Model model, ConfigProjectedCashFlowForPeriodForm form) {
try {
final UriComponentsBuilder transactionBuilder = UriComponentsBuilder
.fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.RT_GET_ALL_DUE_IN_RANGE));
transactionBuilder.queryParam("start", ControllerUtils.formatDate(this.financerConfig, form.getFromDate()));
transactionBuilder.queryParam("end", ControllerUtils.formatDate(this.financerConfig, form.getToDate()));
final Iterable<RecurringTransactionDueInRangeResponseDto> trxs = FinancerRestTemplate.exchangeGet(transactionBuilder,
new ParameterizedTypeReference<Iterable<RecurringTransactionDueInRangeResponseDto>>() {
});
final List<RecurringTransaction> incomeTransactions = new ArrayList<>();
final List<RecurringTransaction> expenseTransactions = new ArrayList<>();
final EnumMap<AccountType, List<AccountType>> bookingRules = new EnumMap<>(AccountType.class);
bookingRules.put(BANK, Arrays.asList(BANK, EXPENSE, LIABILITY));
bookingRules.put(CASH, Arrays.asList(EXPENSE, LIABILITY));
IterableUtils.toList(trxs).stream()
.flatMap(dto -> IterableUtils.toList(dto.getRecurringTransactions()).stream()).forEach(rt -> {
if (AccountType.INCOME == rt.getFromAccount().getType()) {
incomeTransactions.add(rt);
} else if (bookingRules.getOrDefault(rt.getFromAccount().getType(), Collections.EMPTY_LIST)
.contains(rt.getToAccount().getType())) {
expenseTransactions.add(rt);
}
});
ConfigProjectedCashFlowForPeriodTransactionsForm formTransactions =
new ConfigProjectedCashFlowForPeriodTransactionsForm(incomeTransactions, expenseTransactions,
form.isByAccount(), ControllerUtils.parseDate(this.financerConfig, form.getFromDate()),
ControllerUtils.parseDate(this.financerConfig, form.getToDate()));
if(form.isByAccount()) {
final ResponseEntity<Iterable<Account>> response = new GetAllAccountsTemplate().exchange(this.financerConfig);
decorateAccounts(ControllerUtils.filterAndSortAccounts(response.getBody(), false, LIABILITY, EXPENSE))
.forEach(formTransactions::addAccountContainer);
}
else {
// TODO by account group
}
formTransactions.refresh();
model.addAttribute("form", formTransactions);
ControllerUtils.addVersionAttribute(model, this.financerConfig);
ControllerUtils.addCurrencySymbol(model, this.financerConfig);
ControllerUtils.addDarkMode(model, this.financerConfig);
return "report/configureProjectedCashFlowForPeriodTransactions";
} catch (FinancerRestException e) {
model.addAttribute("errorMessage", e.getResponseReason().name());
model.addAttribute("form", form);
ControllerUtils.addVersionAttribute(model, this.financerConfig);
ControllerUtils.addCurrencySymbol(model, this.financerConfig);
ControllerUtils.addDarkMode(model, this.financerConfig);
return "report/configureProjectedCashFlowForPeriod";
}
}
@PostMapping("/configureProjectedCashFlowForPeriodTransactionsRefresh")
public String configureProjectedCashFlowForPeriodTransactionsRefresh(Model model, ConfigProjectedCashFlowForPeriodTransactionsForm form) {
form.refresh();
model.addAttribute("form", form);
ControllerUtils.addVersionAttribute(model, this.financerConfig);
ControllerUtils.addCurrencySymbol(model, this.financerConfig);
ControllerUtils.addDarkMode(model, this.financerConfig);
return "report/configureProjectedCashFlowForPeriodTransactions";
}
private List<AccountDecorator> decorateAccounts(List<Account> accounts) {
return accounts.stream().map(AccountDecorator::new).collect(Collectors.toList());
}
} }

View File

@@ -62,4 +62,8 @@ public class AccountDecorator {
return null; return null;
} }
public Account getAccount() {
return account;
}
} }

View File

@@ -0,0 +1,31 @@
package de.financer.form;
public class ConfigProjectedCashFlowForPeriodForm {
private String fromDate;
private String toDate;
private boolean byAccount;
public String getFromDate() {
return fromDate;
}
public void setFromDate(String fromDate) {
this.fromDate = fromDate;
}
public String getToDate() {
return toDate;
}
public void setToDate(String toDate) {
this.toDate = toDate;
}
public boolean isByAccount() {
return byAccount;
}
public void setByAccount(boolean byAccount) {
this.byAccount = byAccount;
}
}

View File

@@ -0,0 +1,184 @@
package de.financer.form;
import de.financer.decorator.AccountDecorator;
import de.financer.model.Account;
import de.financer.model.RecurringTransaction;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ConfigProjectedCashFlowForPeriodTransactionsForm {
public static class AccountContainer {
private Account account;
private long spending;
private long averageSpending;
public AccountContainer() {
}
public AccountContainer(Account account, long spending, long averageSpending) {
this.account = account;
this.spending = spending;
this.averageSpending = averageSpending;
if(this.spending == 0 && this.averageSpending != 0) {
this.spending = this.averageSpending;
}
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public long getSpending() {
return spending;
}
public void setSpending(long spending) {
this.spending = spending;
}
public long getAverageSpending() {
return averageSpending;
}
public void setAverageSpending(long averageSpending) {
this.averageSpending = averageSpending;
}
}
private List<RecurringTransaction> incomeTransactions;
private List<RecurringTransaction> expenseTransactions;
private List<AccountContainer> accountContainers;
private long incomeSum;
private long expenseSum;
private long cashFlow;
private boolean byAccount;
private long accountSum;
private LocalDate fromDate;
private LocalDate toDate;
public ConfigProjectedCashFlowForPeriodTransactionsForm() {
this.incomeTransactions = new ArrayList<>();
this.expenseTransactions = new ArrayList<>();
this.accountContainers = new ArrayList<>();
}
public ConfigProjectedCashFlowForPeriodTransactionsForm(List<RecurringTransaction> incomeTransactions,
List<RecurringTransaction> expenseTransactions,
boolean byAccount, LocalDate fromDate, LocalDate toDate
) {
this.incomeTransactions = incomeTransactions;
this.expenseTransactions = expenseTransactions;
this.accountContainers = new ArrayList<>();
this.byAccount = byAccount;
this.fromDate = fromDate;
this.toDate = toDate;
Collections.sort(this.incomeTransactions, Comparator.comparing(RecurringTransaction::getAmount));
Collections.sort(this.expenseTransactions, Comparator.comparing(RecurringTransaction::getAmount));
}
public void refresh() {
this.incomeSum = incomeTransactions.stream().mapToLong(RecurringTransaction::getAmount).sum();
this.expenseSum = expenseTransactions.stream().mapToLong(RecurringTransaction::getAmount).sum();
this.cashFlow = this.incomeSum - this.expenseSum;
if(this.byAccount) {
this.accountSum = this.accountContainers.stream().filter(ac -> ac.spending != 0).mapToLong(AccountContainer::getSpending).sum();
this.cashFlow = this.cashFlow - this.accountSum;
}
else {
// TODO account group calc
}
}
public List<RecurringTransaction> getIncomeTransactions() {
return incomeTransactions;
}
public void setIncomeTransactions(List<RecurringTransaction> incomeTransactions) {
this.incomeTransactions = incomeTransactions;
}
public List<RecurringTransaction> getExpenseTransactions() {
return expenseTransactions;
}
public void setExpenseTransactions(List<RecurringTransaction> expenseTransactions) {
this.expenseTransactions = expenseTransactions;
}
public List<AccountContainer> getAccountContainers() {
return accountContainers;
}
public void setAccountContainers(List<AccountContainer> accountContainers) {
this.accountContainers = accountContainers;
}
public void addAccountContainer(AccountDecorator account) {
this.accountContainers.add(new AccountContainer(account.getAccount(), account.getSpendingCurrentExpensePeriod(), account.getAverageSpendingExpensePeriod()));
}
public long getIncomeSum() {
return incomeSum;
}
public void setIncomeSum(long incomeSum) {
this.incomeSum = incomeSum;
}
public long getExpenseSum() {
return expenseSum;
}
public void setExpenseSum(long expenseSum) {
this.expenseSum = expenseSum;
}
public long getCashFlow() {
return cashFlow;
}
public boolean isByAccount() {
return byAccount;
}
public void setByAccount(boolean byAccount) {
this.byAccount = byAccount;
}
public long getAccountSum() {
return accountSum;
}
public void setAccountSum(long accountSum) {
this.accountSum = accountSum;
}
public LocalDate getFromDate() {
return fromDate;
}
public void setFromDate(LocalDate fromDate) {
this.fromDate = fromDate;
}
public LocalDate getToDate() {
return toDate;
}
public void setToDate(LocalDate toDate) {
this.toDate = toDate;
}
}

View File

@@ -0,0 +1,13 @@
package de.financer.form;
public class SelectReportForm {
private String reportType;
public String getReportType() {
return reportType;
}
public void setReportType(String reportType) {
this.reportType = reportType;
}
}

View File

@@ -0,0 +1,17 @@
package de.financer.report;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public enum ReportType {
PROJECTED_CASH_FLOW;
public static List<String> valueList() {
return Arrays.stream(ReportType.values()).map(ReportType::name).collect(Collectors.toList());
}
public static ReportType getByValue(String value) {
return Arrays.stream(values()).filter((ct) -> ct.name().equals(value)).findFirst().get();
}
}

View File

@@ -2,10 +2,7 @@ package de.financer.util;
import de.financer.config.FinancerConfig; import de.financer.config.FinancerConfig;
import de.financer.controller.Function; import de.financer.controller.Function;
import de.financer.model.Account; import de.financer.model.*;
import de.financer.model.AccountGroup;
import de.financer.model.AccountStatus;
import de.financer.model.RecurringTransaction;
import de.financer.util.comparator.AccountByTypeByGroupNameByIdComparator; import de.financer.util.comparator.AccountByTypeByGroupNameByIdComparator;
import org.apache.commons.collections4.IterableUtils; import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -14,6 +11,7 @@ import org.springframework.ui.Model;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle; import java.time.format.FormatStyle;
import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@@ -40,6 +38,15 @@ public class ControllerUtils {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public static List<Account> filterAndSortAccounts(Iterable<Account> accounts, boolean showClosed, AccountType... filterAccountTypes) {
return IterableUtils.toList(accounts).stream()
.filter((acc) -> AccountStatus.OPEN
.equals(acc.getStatus()) || showClosed)
.filter(acc -> Arrays.asList(filterAccountTypes).contains(acc.getType()))
.sorted(new AccountByTypeByGroupNameByIdComparator())
.collect(Collectors.toList());
}
public static List<AccountGroup> sortAccountGroups(Iterable<AccountGroup> accountGroups) { public static List<AccountGroup> sortAccountGroups(Iterable<AccountGroup> accountGroups) {
return IterableUtils.toList(accountGroups).stream() return IterableUtils.toList(accountGroups).stream()
.sorted(Comparator.comparing(AccountGroup::getName)) .sorted(Comparator.comparing(AccountGroup::getName))

View File

@@ -10,6 +10,7 @@ financer.account-overview.available-actions.create-recurring-transaction=Create
financer.account-overview.available-actions.recurring-transaction-all=Show all recurring transactions financer.account-overview.available-actions.recurring-transaction-all=Show all recurring transactions
financer.account-overview.available-actions.create-account-group=Create new account group financer.account-overview.available-actions.create-account-group=Create new account group
financer.account-overview.available-actions.select-chart=Generate a chart financer.account-overview.available-actions.select-chart=Generate a chart
financer.account-overview.available-actions.select-report=Generate a report
financer.account-overview.available-actions.close-current-period=Close the current expense period financer.account-overview.available-actions.close-current-period=Close the current expense period
financer.account-overview.available-actions.recurring-transaction-calendar=Recurring transaction calendar financer.account-overview.available-actions.recurring-transaction-calendar=Recurring transaction calendar
financer.account-overview.available-actions.period-overview=Period overview financer.account-overview.available-actions.period-overview=Period overview
@@ -215,6 +216,17 @@ financer.chart-config-account-expenses-for-period.label.from-date=From date\:
financer.chart-config-account-expenses-for-period.label.to-date=To date\: financer.chart-config-account-expenses-for-period.label.to-date=To date\:
financer.chart-config-account-expenses-for-period.submit=Generate financer.chart-config-account-expenses-for-period.submit=Generate
financer.report-config-projected-cashflow-for-period.title=Configure projected cash flow report
financer.report-config-projected-cashflow-for-period.label.from-date=From date\:
financer.report-config-projected-cashflow-for-period.label.to-date=To date\:
financer.report-config-projected-cashflow-for-period.label.account-or-account-group-summary=By account or account group
financer.report-config-projected-cashflow-for-period.label.by-account=Account
financer.report-config-projected-cashflow-for-period.label.by-account-group=Account group
financer.report-config-projected-cashflow-for-period.submit=Next step
financer.report-select.title=Select a report to generate
financer.report-select.submit=Select
financer.period-overview.title=Period overview financer.period-overview.title=Period overview
financer.period-overview.table-header.id=ID financer.period-overview.table-header.id=ID
financer.period-overview.table-header.type=Period type financer.period-overview.table-header.type=Period type
@@ -276,6 +288,8 @@ financer.heading.period-overview=financer\: period overview
financer.heading.upload-transactions=financer\: upload transactions financer.heading.upload-transactions=financer\: upload transactions
financer.heading.create-upload-transactions=financer\: create uploaded transactions financer.heading.create-upload-transactions=financer\: create uploaded transactions
financer.heading.account-edit=financer\: edit account financer.heading.account-edit=financer\: edit account
financer.heading.report-select=financer\: select a report to generate
financer.heading.report-config-projected-cashflow-for-period=financer\: configure projected cash flow report
financer.cancel-back-to-overview=Cancel and back to overview financer.cancel-back-to-overview=Cancel and back to overview
financer.back-to-overview=Back to overview financer.back-to-overview=Back to overview
@@ -298,6 +312,8 @@ financer.chart.name.ACCOUNT_EXPENSES_FOR_PERIOD=Expenses for a configurable peri
financer.chart.name.EXPENSE_PERIOD_TOTALS_CURRENT_YEAR=Overview about the income and expenses in the current year (bar chart) financer.chart.name.EXPENSE_PERIOD_TOTALS_CURRENT_YEAR=Overview about the income and expenses in the current year (bar chart)
financer.chart.name.EXPENSE_PERIOD_TOTALS_FOR_YEAR=Overview about the income and expenses for a configurable year (bar chart) financer.chart.name.EXPENSE_PERIOD_TOTALS_FOR_YEAR=Overview about the income and expenses for a configurable year (bar chart)
financer.report.name.PROJECTED_CASH_FLOW=Projected cash flow
financer.recurring-transaction-calendar.title=financer\: recurring transaction calendar financer.recurring-transaction-calendar.title=financer\: recurring transaction calendar
financer.calendar.calendarweek=CW financer.calendar.calendarweek=CW
financer.calendar.weekdays.MONDAY=Monday financer.calendar.weekdays.MONDAY=Monday
@@ -346,4 +362,14 @@ financer.error-message.ACCOUNT_GROUP_NOT_FOUND=The account group could not be fo
financer.error-message.UNKNOWN_CHART_TYPE=The selected chart type is not known! financer.error-message.UNKNOWN_CHART_TYPE=The selected chart type is not known!
financer.error-message.INVALID_HAS_FILE_VALUE=The value for parameter hasFile cannot be parsed! financer.error-message.INVALID_HAS_FILE_VALUE=The value for parameter hasFile cannot be parsed!
financer.error-message.INVALID_FILE_CONTENT=File content is missing! financer.error-message.INVALID_FILE_CONTENT=File content is missing!
financer.error-message.INVALID_FILE_NAME=File name is missing! financer.error-message.INVALID_FILE_NAME=File name is missing!
financer.report-config-projected-cashflow-for-period-transactions.title=Configure projected cash flow report
financer.heading.report-config-projected-cashflow-for-period-transactions=financer\: configure projected cash flow report
financer.report-config-projected-cashflow-for-period-transactions.table.income.sum=Income sum
financer.report-config-projected-cashflow-for-period-transactions.table.expense.sum=Expense sum
financer.report-config-projected-cashflow-for-period-transactions.table.accounts.sum=Accounts sum
financer.report-config-projected-cashflow-for-period-transactions.table.cash-flow.sum=Cash flow sum
financer.report-config-projected-cashflow-for-period-transactions.label.button.refresh=Refresh
financer.report-config-projected-cashflow-for-period-transactions.label.button.create=Create report
financer.heading.report-config-projected-cashflow-for-period-transactions-print=financer\: projected cash flow report

View File

@@ -8,8 +8,9 @@ financer.account-overview.available-actions.search-transactions=Buchungen suchen
financer.account-overview.available-actions.upload-transactions=Buchungen hochladen financer.account-overview.available-actions.upload-transactions=Buchungen hochladen
financer.account-overview.available-actions.create-recurring-transaction=Neue wiederkehrende Buchung erstellen financer.account-overview.available-actions.create-recurring-transaction=Neue wiederkehrende Buchung erstellen
financer.account-overview.available-actions.recurring-transaction-all=Zeige alle wiederkehrende Buchungen financer.account-overview.available-actions.recurring-transaction-all=Zeige alle wiederkehrende Buchungen
financer.account-overview.available-actions.create-account-group=Neue Konto-Gruppe erstellen financer.account-overview.available-actions.create-account-group=Neue Kontogruppe erstellen
financer.account-overview.available-actions.select-chart=Ein Diagramm erzeugen financer.account-overview.available-actions.select-chart=Ein Diagramm erzeugen
financer.account-overview.available-actions.select-report=Einen Bericht erzeugen
financer.account-overview.available-actions.close-current-period=Aktuelle Periode schlie\u00DFen financer.account-overview.available-actions.close-current-period=Aktuelle Periode schlie\u00DFen
financer.account-overview.available-actions.recurring-transaction-calendar=Kalender wiederkehrende Buchung financer.account-overview.available-actions.recurring-transaction-calendar=Kalender wiederkehrende Buchung
financer.account-overview.available-actions.period-overview=Perioden\u00FCbersicht financer.account-overview.available-actions.period-overview=Perioden\u00FCbersicht
@@ -26,7 +27,7 @@ financer.account-overview.table-header.spending-current-period=Ausgaben aktuelle
financer.account-overview.table-header.average-spending-period=Durchschnittliche Ausgaben financer.account-overview.table-header.average-spending-period=Durchschnittliche Ausgaben
financer.account-overview.table-header.type=Typ financer.account-overview.table-header.type=Typ
financer.account-overview.table-header.status=Status financer.account-overview.table-header.status=Status
financer.account-overview.tooltip.status.current-expenses=Periode ab {0}. Durch Klicken des Betrags kann eine grafische \u00DCbersicht über die Ausgaben gruppiert nach Konto-Gruppe angezeigt werden financer.account-overview.tooltip.status.current-expenses=Periode ab {0}. Durch Klicken des Betrags kann eine grafische \u00DCbersicht über die Ausgaben gruppiert nach Kontogruppe angezeigt werden
financer.account-overview.tooltip.status.current-assets=Kurzfristig verf\u00FCgbares Verm\u00F6gen financer.account-overview.tooltip.status.current-assets=Kurzfristig verf\u00FCgbares Verm\u00F6gen
financer.account-overview.expense-history.description=Ausgabenhistorie financer.account-overview.expense-history.description=Ausgabenhistorie
@@ -37,9 +38,9 @@ financer.account-new.label.group=Gruppe\:
financer.account-new.label.upload-match-regexps=Zuordnungsregexps\: financer.account-new.label.upload-match-regexps=Zuordnungsregexps\:
financer.account-new.submit=Konto erstellen financer.account-new.submit=Konto erstellen
financer.account-group-new.title=financer\: Neue Konto-Gruppe erstellen financer.account-group-new.title=financer\: Neue Kontogruppe erstellen
financer.account-group-new.label.name=Name\: financer.account-group-new.label.name=Name\:
financer.account-group-new.submit=Konto-Gruppe erstellen financer.account-group-new.submit=Kontogruppe erstellen
financer.account-edit.title=financer\: Bearbeite Konto financer.account-edit.title=financer\: Bearbeite Konto
financer.account-edit.label.key=Schl\u00FCssel\: financer.account-edit.label.key=Schl\u00FCssel\:
@@ -205,7 +206,7 @@ financer.create-upload-transactions.submit=Erstelle hochgeladene Buchungen
financer.chart-select.title=Ein Diagramm zum Erzeugen ausw\u00E4hlen financer.chart-select.title=Ein Diagramm zum Erzeugen ausw\u00E4hlen
financer.chart-select.submit=Ausw\u00E4hlen financer.chart-select.submit=Ausw\u00E4hlen
financer.chart-config-account-group-expenses-for-period.title=Konfigurieren von Ausgaben f\u00FCr Periode gruppiert nach Konto-Gruppe Diagramm financer.chart-config-account-group-expenses-for-period.title=Konfigurieren von Ausgaben f\u00FCr Periode gruppiert nach Kontogruppe Diagramm
financer.chart-config-account-group-expenses-for-period.label.from-date=Von Datum\: financer.chart-config-account-group-expenses-for-period.label.from-date=Von Datum\:
financer.chart-config-account-group-expenses-for-period.label.to-date=Bis Datum\: financer.chart-config-account-group-expenses-for-period.label.to-date=Bis Datum\:
financer.chart-config-account-group-expenses-for-period.submit=Erzeugen financer.chart-config-account-group-expenses-for-period.submit=Erzeugen
@@ -215,6 +216,17 @@ financer.chart-config-account-expenses-for-period.label.from-date=Von Datum\:
financer.chart-config-account-expenses-for-period.label.to-date=Bis Datum\: financer.chart-config-account-expenses-for-period.label.to-date=Bis Datum\:
financer.chart-config-account-expenses-for-period.submit=Erzeugen financer.chart-config-account-expenses-for-period.submit=Erzeugen
financer.report-config-projected-cashflow-for-period.title=Konfigurieren von prognostizierter Cashflow Bericht
financer.report-config-projected-cashflow-for-period.label.from-date=Von Datum\:
financer.report-config-projected-cashflow-for-period.label.to-date=Bis Datum\:
financer.report-config-projected-cashflow-for-period.label.account-or-account-group-summary=Konto oder Kontogruppe
financer.report-config-projected-cashflow-for-period.label.by-account=Konto
financer.report-config-projected-cashflow-for-period.label.by-account-group=Kontogruppe
financer.report-config-projected-cashflow-for-period.submit=N\u00E4chster Schritt
financer.report-select.title=Einen Bericht zum Erzeugen ausw\u00E4hlen
financer.report-select.submit=Ausw\u00E4hlen
financer.period-overview.title=Perioden\u00FCbersicht financer.period-overview.title=Perioden\u00FCbersicht
financer.period-overview.table-header.id=ID financer.period-overview.table-header.id=ID
financer.period-overview.table-header.type=Periodentyp financer.period-overview.table-header.type=Periodentyp
@@ -260,7 +272,7 @@ financer.period-type.GRAND_TOTAL=Gesamtausgaben
financer.heading.transaction-new=financer\: Neue Buchung erstellen financer.heading.transaction-new=financer\: Neue Buchung erstellen
financer.heading.recurring-transaction-new=financer\: Neue wiederkehrende Buchung erstellen financer.heading.recurring-transaction-new=financer\: Neue wiederkehrende Buchung erstellen
financer.heading.account-new=financer\: Neues Konto erstellen financer.heading.account-new=financer\: Neues Konto erstellen
financer.heading.account-group-new=financer\: Neue Konto-Gruppe erstellen financer.heading.account-group-new=financer\: Neue Kontogruppe erstellen
financer.heading.account-overview=financer\: \u00DCbersicht financer.heading.account-overview=financer\: \u00DCbersicht
financer.heading.account-details=financer\: Kontodetails f\u00FCr {0} financer.heading.account-details=financer\: Kontodetails f\u00FCr {0}
financer.heading.recurring-transaction-list.dueToday=financer\: Wiederkehrende Buchungen heute f\u00E4llig financer.heading.recurring-transaction-list.dueToday=financer\: Wiederkehrende Buchungen heute f\u00E4llig
@@ -268,21 +280,23 @@ financer.heading.recurring-transaction-list.active=financer\: Aktive wiederkehre
financer.heading.recurring-transaction-list.all=financer\: Alle wiederkehrende Buchungen financer.heading.recurring-transaction-list.all=financer\: Alle wiederkehrende Buchungen
financer.heading.recurring-to-transaction-with-override=financer\: Buchung mit \u00DCberschreibungen aus wiederkehrender Buchung erstellen financer.heading.recurring-to-transaction-with-override=financer\: Buchung mit \u00DCberschreibungen aus wiederkehrender Buchung erstellen
financer.heading.chart-select=financer\: Ein Diagramm zum Erzeugen ausw\u00E4hlen financer.heading.chart-select=financer\: Ein Diagramm zum Erzeugen ausw\u00E4hlen
financer.heading.chart-config-account-group-expenses-for-period=financer\: Konfigurieren von Ausgaben f\u00FCr Periode gruppiert nach Konto-Gruppe Diagramm financer.heading.chart-config-account-group-expenses-for-period=financer\: Konfigurieren von Ausgaben f\u00FCr Periode gruppiert nach Kontogruppe Diagramm
financer.heading.search-transactions=financer\: Buchungen suchen financer.heading.search-transactions=financer\: Buchungen suchen
financer.heading.recurring-transaction-calendar=financer\: Kalender wiederkehrende Buchung financer.heading.recurring-transaction-calendar=financer\: Kalender wiederkehrende Buchung
financer.heading.period-overview=financer\: Perioden\u00FCbersicht financer.heading.period-overview=financer\: Perioden\u00FCbersicht
financer.heading.upload-transactions=financer\: Buchungen hochladen financer.heading.upload-transactions=financer\: Buchungen hochladen
financer.heading.create-upload-transactions=financer\: Erstelle hochgeladene Buchungen financer.heading.create-upload-transactions=financer\: Erstelle hochgeladene Buchungen
financer.heading.account-edit=financer\: Bearbeite Konto financer.heading.account-edit=financer\: Bearbeite Konto
financer.heading.report-select=financer\: Einen Bericht zum Erzeugen ausw\u00E4hlen
financer.heading.report-config-projected-cashflow-for-period=financer\: Konfigurieren von prognostizierter Cashflow Bericht
financer.cancel-back-to-overview=Abbrechen und zur\u00FCck zur \u00DCbersicht financer.cancel-back-to-overview=Abbrechen und zur\u00FCck zur \u00DCbersicht
financer.back-to-overview=Zur\u00FCck zur \u00DCbersicht financer.back-to-overview=Zur\u00FCck zur \u00DCbersicht
financer.show-actions=Anzeigen... financer.show-actions=Anzeigen...
financer.show-options=Zeige Optionen... financer.show-options=Zeige Optionen...
financer.chart.account-group-expenses-current-period.title=Ausgaben in der aktuellen Periode gruppiert nach Konto-Gruppe financer.chart.account-group-expenses-current-period.title=Ausgaben in der aktuellen Periode gruppiert nach Kontogruppe
financer.chart.account-group-expenses-for-period.title=Ausgaben in der Periode vom {0} bis {1} gruppiert nach Konto-Gruppe financer.chart.account-group-expenses-for-period.title=Ausgaben in der Periode vom {0} bis {1} gruppiert nach Kontogruppe
financer.chart.account-expenses-current-period.title=Ausgaben in der aktuellen Periode gruppiert nach Konto financer.chart.account-expenses-current-period.title=Ausgaben in der aktuellen Periode gruppiert nach Konto
financer.chart.account-expenses-for-period.title=Ausgaben in der Periode vom {0} bis {1} gruppiert nach Konto financer.chart.account-expenses-for-period.title=Ausgaben in der Periode vom {0} bis {1} gruppiert nach Konto
financer.chart.expense-period-totals-current-year.title=Gesamtbetr\u00E4ge gruppiert nach Periode für das aktuelle Jahr financer.chart.expense-period-totals-current-year.title=Gesamtbetr\u00E4ge gruppiert nach Periode für das aktuelle Jahr
@@ -290,13 +304,15 @@ financer.chart.expense-period-totals-current-year.x=Betrag
financer.chart.expense-period-totals-current-year.y=Periode financer.chart.expense-period-totals-current-year.y=Periode
financer.chart.expense-period-totals-current-year.open-period=OFFEN financer.chart.expense-period-totals-current-year.open-period=OFFEN
financer.chart.name.ACCOUNT_GROUP_EXPENSES_CURRENT_PERIOD=Ausgaben in der aktuellen Periode gruppiert nach Konto-Gruppe (Kuchendiagramm) financer.chart.name.ACCOUNT_GROUP_EXPENSES_CURRENT_PERIOD=Ausgaben in der aktuellen Periode gruppiert nach Kontogruppe (Kuchendiagramm)
financer.chart.name.ACCOUNT_GROUP_EXPENSES_FOR_PERIOD=Ausgaben f\u00FCr eine konfigurierbare Periode gruppiert nach Konto-Gruppe (Kuchendiagramm) financer.chart.name.ACCOUNT_GROUP_EXPENSES_FOR_PERIOD=Ausgaben f\u00FCr eine konfigurierbare Periode gruppiert nach Kontogruppe (Kuchendiagramm)
financer.chart.name.ACCOUNT_EXPENSES_CURRENT_PERIOD=Ausgaben in der aktuellen Periode gruppiert nach Konto (Kuchendiagramm) financer.chart.name.ACCOUNT_EXPENSES_CURRENT_PERIOD=Ausgaben in der aktuellen Periode gruppiert nach Konto (Kuchendiagramm)
financer.chart.name.ACCOUNT_EXPENSES_FOR_PERIOD=Ausgaben f\u00FCr eine konfigurierbare Periode gruppiert nach Konto (Kuchendiagramm) financer.chart.name.ACCOUNT_EXPENSES_FOR_PERIOD=Ausgaben f\u00FCr eine konfigurierbare Periode gruppiert nach Konto (Kuchendiagramm)
financer.chart.name.EXPENSE_PERIOD_TOTALS_CURRENT_YEAR=\u00DCbersicht über das Einkommen und die Ausgaben im laufenden Jahr (Balkendiagramm) financer.chart.name.EXPENSE_PERIOD_TOTALS_CURRENT_YEAR=\u00DCbersicht über das Einkommen und die Ausgaben im laufenden Jahr (Balkendiagramm)
financer.chart.name.EXPENSE_PERIOD_TOTALS_FOR_YEAR=\u00DCbersicht über das Einkommen und die Ausgaben für ein konfigurierbares Jahr (Balkendiagramm) financer.chart.name.EXPENSE_PERIOD_TOTALS_FOR_YEAR=\u00DCbersicht über das Einkommen und die Ausgaben für ein konfigurierbares Jahr (Balkendiagramm)
financer.report.name.PROJECTED_CASH_FLOW=Prognostizierter Cashflow
financer.recurring-transaction-calendar.title=financer\: Kalender wiederkehrende Buchung financer.recurring-transaction-calendar.title=financer\: Kalender wiederkehrende Buchung
financer.calendar.calendarweek=KW financer.calendar.calendarweek=KW
financer.calendar.weekdays.MONDAY=Montag financer.calendar.weekdays.MONDAY=Montag
@@ -340,9 +356,19 @@ financer.error-message.INVALID_TRANSACTION_ID=Die ausgew\u00E4hlte Buchung ist u
financer.error-message.TRANSACTION_NOT_FOUND=Die ausgew\u00E4hlte Buchung wurde nicht gefunden! financer.error-message.TRANSACTION_NOT_FOUND=Die ausgew\u00E4hlte Buchung wurde nicht gefunden!
financer.error-message.ACCOUNT_NOT_FOUND=Das ausgew\u00E4hlte Konto wurde nicht gefunden! financer.error-message.ACCOUNT_NOT_FOUND=Das ausgew\u00E4hlte Konto wurde nicht gefunden!
financer.error-message.DUPLICATE_ACCOUNT_KEY=Ein Konto mit diesem Schl\u00FCssel existiert bereits! financer.error-message.DUPLICATE_ACCOUNT_KEY=Ein Konto mit diesem Schl\u00FCssel existiert bereits!
financer.error-message.DUPLICATE_ACCOUNT_GROUP_NAME=Eine Konto-Gruppe mit diesem Namen existiert bereits! financer.error-message.DUPLICATE_ACCOUNT_GROUP_NAME=Eine Kontogruppe mit diesem Namen existiert bereits!
financer.error-message.ACCOUNT_GROUP_NOT_FOUND=Die ausgew\u00E4hlte Konto-Gruppe wurde nicht gefunden! financer.error-message.ACCOUNT_GROUP_NOT_FOUND=Die ausgew\u00E4hlte Kontogruppe wurde nicht gefunden!
financer.error-message.UNKNOWN_CHART_TYPE=Das ausgew\u00E4hlte Diagramm wurde nicht gefunden! financer.error-message.UNKNOWN_CHART_TYPE=Das ausgew\u00E4hlte Diagramm wurde nicht gefunden!
financer.error-message.INVALID_HAS_FILE_VALUE=Der Wert des Parameters hasFile kann nicht verarbeitet werden! financer.error-message.INVALID_HAS_FILE_VALUE=Der Wert des Parameters hasFile kann nicht verarbeitet werden!
financer.error-message.INVALID_FILE_CONTENT=Der Inhalt der Datei fehlt! financer.error-message.INVALID_FILE_CONTENT=Der Inhalt der Datei fehlt!
financer.error-message.INVALID_FILE_NAME=Der Dateiname fehlt! financer.error-message.INVALID_FILE_NAME=Der Dateiname fehlt!
financer.report-config-projected-cashflow-for-period-transactions.title=Konfigurieren von prognostizierter Cashflow Bericht
financer.heading.report-config-projected-cashflow-for-period-transactions=financer\: Konfigurieren von prognostizierter Cashflow Bericht
financer.report-config-projected-cashflow-for-period-transactions.table.income.sum=Einkommen
financer.report-config-projected-cashflow-for-period-transactions.table.expense.sum=Wiederkehrende Buchungen
financer.report-config-projected-cashflow-for-period-transactions.table.accounts.sum=Konten
financer.report-config-projected-cashflow-for-period-transactions.table.cash-flow.sum=Cashflow
financer.report-config-projected-cashflow-for-period-transactions.label.button.refresh=Aktualisieren
financer.report-config-projected-cashflow-for-period-transactions.label.button.create=Bericht erstellen
financer.heading.report-config-projected-cashflow-for-period-transactions-print=financer\: Prognostizierter Cashflow Bericht

View File

@@ -1,5 +1,5 @@
v49 -> v50: v49 -> v50:
- - #29 Added projected cash flow report
v48 -> v49: v48 -> v49:
- #27 The recurring transaction selection during transaction import now contains all recurring transaction instead of - #27 The recurring transaction selection during transaction import now contains all recurring transaction instead of

View File

@@ -72,7 +72,8 @@ a {
#transaction-table, #transaction-table,
#recurring-transaction-list-table, #recurring-transaction-list-table,
#period-overview-table, #period-overview-table,
#create-upload-transactions-table { #create-upload-transactions-table,
#report-config-projected-cashflow-for-period-transactions-table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
text-align: left; text-align: left;
@@ -89,7 +90,8 @@ a {
#period-overview-table th, #period-overview-table th,
#period-overview-table td, #period-overview-table td,
#create-upload-transactions-table th, #create-upload-transactions-table th,
#create-upload-transactions-table td { #create-upload-transactions-table td,
#report-config-projected-cashflow-for-period-transactions-table td {
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
padding: 0.3em; padding: 0.3em;
vertical-align: top; vertical-align: top;
@@ -174,6 +176,7 @@ tr:hover {
#new-account-group-form *, #new-account-group-form *,
#chart-config-account-group-expenses-for-period-form *, #chart-config-account-group-expenses-for-period-form *,
#chart-config-account-expenses-for-period-form *, #chart-config-account-expenses-for-period-form *,
#report-config-projected-cashflow-for-period-form *,
#search-transactions-form *, #search-transactions-form *,
#upload-transactions-form *, #upload-transactions-form *,
#edit-account-form * { #edit-account-form * {
@@ -201,6 +204,12 @@ tr:hover {
display: block; display: block;
} }
#report-select-form div {
width: 20em;
margin-top: 1em;
display: block;
}
input[type=submit] { input[type=submit] {
margin-top: 1em; margin-top: 1em;
margin-bottom: 1em; margin-bottom: 1em;
@@ -397,4 +406,64 @@ input[type=submit] {
.transaction-upload-fieldset * { .transaction-upload-fieldset * {
width: 17.75em !important; width: 17.75em !important;
}
.report-config-projected-cashflow-fieldset {
margin-left: 1em;
margin-top: 0px !important;
}
.report-config-projected-cashflow-fieldset * {
width: 17.75em !important;
}
.display-none {
display: none;
}
* {
font-family: 'Source Code Pro', monospace;
}
.projected-cashflow-transaction {
padding-left: 1em !important;
}
.projected-cashflow-sum > * {
font-weight: bolder;
padding-top: 1em !important;
}
#report-config-projected-cashflow-for-period-transactions-table {
width: 55% !important;
}
.report-config-projected-cashflow-for-period-transactions-table-amount {
width: 5em;
text-align: right;
}
.print {
display: none;
}
@media print{
.noprint{
display: none !important;
}
.print {
display: table-cell !important;
}
.print-padding {
padding-top: 1em;
padding-left: 1em;
display: block !important;
}
}
.report-config-projected-cashflow-for-period-transactions-table-average-spending {
width: 13em;
text-align: right;
} }

View File

@@ -73,6 +73,8 @@
<div id="action-container-sub-reports"> <div id="action-container-sub-reports">
<a th:href="@{/selectChart}" <a th:href="@{/selectChart}"
th:text="#{financer.account-overview.available-actions.select-chart}"/> th:text="#{financer.account-overview.available-actions.select-chart}"/>
<a th:href="@{/selectReport}"
th:text="#{financer.account-overview.available-actions.select-report}"/>
</div> </div>
</div> </div>
<table id="account-overview-table"> <table id="account-overview-table">

View File

@@ -1,4 +1,4 @@
<div id="footer-container" th:fragment="footer"> <div id="footer-container" th:fragment="footer" class="noprint">
<hr> <hr>
<div> <div>
<span th:text="'financer v' + ${financerVersion}"/> <span th:text="'financer v' + ${financerVersion}"/>

View File

@@ -0,0 +1,36 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="#{financer.report-config-projected-cashflow-for-period.title}"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link th:if="${darkMode}" rel="stylesheet" th:href="@{/css/darkModeColors.css}"/>
<link th:if="${!darkMode}" rel="stylesheet" th:href="@{/css/lightModeColors.css}"/>
<link rel="stylesheet" th:href="@{/css/main.css}">
<link rel="shortcut icon" th:href="@{/favicon.ico}"/>
</head>
<body>
<h1 th:text="#{financer.heading.report-config-projected-cashflow-for-period}"/>
<span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/>
<a th:href="@{/accountOverview}" th:text="#{financer.cancel-back-to-overview}"/>
<form id="report-config-projected-cashflow-for-period-form" action="#"
th:action="@{/configureProjectedCashFlowForPeriodTransactions}" th:object="${form}" method="post">
<label for="fromDate" th:text="#{financer.report-config-projected-cashflow-for-period.label.from-date}"/>
<input type="date" id="fromDate" th:field="*{fromDate}"/>
<label for="toDate" th:text="#{financer.report-config-projected-cashflow-for-period.label.to-date}"/>
<input type="date" id="toDate" th:field="*{toDate}"/>
<details>
<summary th:text="#{financer.show-options}"/>
<fieldset class="report-config-projected-cashflow-fieldset">
<legend th:text="#{financer.report-config-projected-cashflow-for-period.label.account-or-account-group-summary}"/>
<label for="by-account" th:text="#{financer.report-config-projected-cashflow-for-period.label.by-account}"/>
<input type="radio" th:value="${true}" th:field="*{byAccount}" id="by-account" checked />
<label for="by-account-group" th:text="#{financer.report-config-projected-cashflow-for-period.label.by-account-group}"/>
<input type="radio" th:value="${false}" th:field="*{byAccount}" id="by-account-group"/>
</fieldset>
</details>
<input type="submit" th:value="#{financer.report-config-projected-cashflow-for-period.submit}"/>
</form>
<div th:replace="includes/footer :: footer"/>
</body>
</html>

View File

@@ -0,0 +1,94 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="#{financer.report-config-projected-cashflow-for-period-transactions.title}"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link th:if="${darkMode}" rel="stylesheet" th:href="@{/css/darkModeColors.css}"/>
<link th:if="${!darkMode}" rel="stylesheet" th:href="@{/css/lightModeColors.css}"/>
<link rel="stylesheet" th:href="@{/css/main.css}">
<link rel="shortcut icon" th:href="@{/favicon.ico}"/>
</head>
<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-print}" class="print"/>
<span th:text="${#temporals.format(form.fromDate) + ' - ' + #temporals.format(form.toDate)}" 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"/>
<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">
<tr class="projected-cashflow-sum">
<td th:text="#{financer.report-config-projected-cashflow-for-period-transactions.table.income.sum}"/>
<td th:utext="${#numbers.formatDecimal(form.incomeSum/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"
class="projected-cashflow-sum report-config-projected-cashflow-for-period-transactions-table-amount"/>
</tr>
<tr th:each="entry, i : ${form.incomeTransactions}">
<td class="projected-cashflow-transaction">
<span th:text="*{incomeTransactions[__${i.index}__].description}" />
<input type="hidden" th:field="*{incomeTransactions[__${i.index}__].description}" class="display-none" />
</td>
<td class="report-config-projected-cashflow-for-period-transactions-table-amount noprint">
<input type="text" th:field="*{incomeTransactions[__${i.index}__].amount}" />
</td>
<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}"/>
</td>
</tr>
<tr class="projected-cashflow-sum">
<td th:text="#{financer.report-config-projected-cashflow-for-period-transactions.table.expense.sum}"/>
<td th:utext="${#numbers.formatDecimal((form.expenseSum/100D)*-1, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"
class="report-config-projected-cashflow-for-period-transactions-table-amount"/>
</tr>
<tr th:each="entry, i : ${form.expenseTransactions}">
<td class="projected-cashflow-transaction">
<span th:text="*{expenseTransactions[__${i.index}__].description}" />
<input type="hidden" th:field="*{expenseTransactions[__${i.index}__].description}" class="display-none" />
</td>
<td class="report-config-projected-cashflow-for-period-transactions-table-amount noprint">
<input type="text" th:field="*{expenseTransactions[__${i.index}__].amount}" />
</td>
<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}"/>
</td>
</tr>
<tr class="projected-cashflow-sum">
<td th:text="#{financer.report-config-projected-cashflow-for-period-transactions.table.accounts.sum}"/>
<td th:utext="${#numbers.formatDecimal((form.accountSum/100D)*-1, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"
class="report-config-projected-cashflow-for-period-transactions-table-amount"/>
<td th:text="#{financer.account-overview.table-header.average-spending-period}"
class="report-config-projected-cashflow-for-period-transactions-table-average-spending noprint"/>
</tr>
<tr th:each="entry, i : ${form.accountContainers}">
<td class="projected-cashflow-transaction">
<span th:text="*{accountContainers[__${i.index}__].account.key}" />
<input type="hidden" th:field="*{accountContainers[__${i.index}__].account.key}" class="display-none" />
</td>
<td class="report-config-projected-cashflow-for-period-transactions-table-amount noprint">
<input type="text" th:field="*{accountContainers[__${i.index}__].spending}" />
</td>
<td class="report-config-projected-cashflow-for-period-transactions-table-amount print">
<span th:utext="${#numbers.formatDecimal(form.accountContainers[__${i.index}__].spending/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
</td>
<td class="noprint report-config-projected-cashflow-for-period-transactions-table-average-spending">
<span th:utext="${#numbers.formatDecimal(form.accountContainers[__${i.index}__].averageSpending/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
<input type="hidden" th:field="*{accountContainers[__${i.index}__].averageSpending}" class="display-none" />
</td>
</tr>
<tr class="projected-cashflow-sum">
<td th:text="#{financer.report-config-projected-cashflow-for-period-transactions.table.cash-flow.sum}"/>
<td th:utext="${#numbers.formatDecimal(form.cashFlow/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"
th:classappend="${form.cashFlow == 0 ? 'color-neutral' : form.cashFlow < 0 ? 'color-bad' : 'color-good'}"
class="report-config-projected-cashflow-for-period-transactions-table-amount"/>
</tr>
</table>
<input type="hidden" th:field="*{fromDate}" class="display-none"/>
<input type="hidden" th:field="*{toDate}" class="display-none"/>
<input type="hidden" th:field="*{byAccount}" class="display-none"/>
<input type="submit" th:formaction="@{/configureProjectedCashFlowForPeriodTransactionsRefresh}"
th:value="#{financer.report-config-projected-cashflow-for-period-transactions.label.button.refresh}" class="noprint"/>
<input type="submit" onClick="window.print()"
th:value="#{financer.report-config-projected-cashflow-for-period-transactions.label.button.create}" class="noprint"/>
</form>
<div th:replace="includes/footer :: footer" />
</body>
</html>

View File

@@ -0,0 +1,27 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="#{financer.report-select.title}"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link th:if="${darkMode}" rel="stylesheet" th:href="@{/css/darkModeColors.css}" />
<link th:if="${!darkMode}" rel="stylesheet" th:href="@{/css/lightModeColors.css}" />
<link rel="stylesheet" th:href="@{/css/main.css}">
<link rel="shortcut icon" th:href="@{/favicon.ico}" />
</head>
<body>
<h1 th:text="#{financer.heading.report-select}"/>
<span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/>
<a th:href="@{/accountOverview}" th:text="#{financer.cancel-back-to-overview}"/>
<form id="report-select-form" action="#" th:action="@{/configureReport}" th:object="${form}" method="post">
<th:block th:each="report, iterStat : ${availableReports}">
<div>
<input type="radio" th:value="${report}" th:field="*{reportType}" th:id="'report' + ${iterStat.count}"/>
<label th:for="'report' + ${iterStat.count}" th:text="#{${'financer.report.name.' + report}}"/>
</div>
</th:block>
<input type="submit" th:value="#{financer.report-select.submit}"/>
</form>
<div th:replace="includes/footer :: footer"/>
</body>
</html>