diff --git a/financer-web-client/src/main/java/de/financer/controller/ReportController.java b/financer-web-client/src/main/java/de/financer/controller/ReportController.java index 79a913f..2d68150 100644 --- a/financer-web-client/src/main/java/de/financer/controller/ReportController.java +++ b/financer-web-client/src/main/java/de/financer/controller/ReportController.java @@ -2,17 +2,32 @@ package de.financer.controller; import de.financer.chart.ChartType; import de.financer.config.FinancerConfig; -import de.financer.form.ConfigAccountExpenseForPeriodForm; -import de.financer.form.ConfigAccountGroupExpenseForPeriodForm; -import de.financer.form.SelectChartForm; +import de.financer.decorator.AccountDecorator; +import de.financer.dto.RecurringTransactionDueInRangeResponseDto; +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 org.apache.commons.collections4.IterableUtils; 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.ui.Model; import org.springframework.web.bind.annotation.GetMapping; 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 public class ReportController { @@ -36,8 +51,7 @@ public class ReportController { try { selectedChartType = ChartType.getByValue(form.getChartType()); - } - catch (NoSuchElementException nsee) { + } catch (NoSuchElementException nsee) { model.addAttribute("form", new SelectChartForm()); model.addAttribute("errorMessage", "UNKNOWN_CHART_TYPE"); model.addAttribute("availableCharts", ChartType.valueList(true)); @@ -48,7 +62,7 @@ public class ReportController { return "report/selectChart"; } - switch(selectedChartType) { + switch (selectedChartType) { case ACCOUNT_GROUP_EXPENSES_CURRENT_PERIOD: // Special case: this chart does not require parameters, so we can // directly redirect to the actual chart instead of the config page @@ -80,4 +94,126 @@ public class ReportController { 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 trxs = FinancerRestTemplate.exchangeGet(transactionBuilder, + new ParameterizedTypeReference>() { + }); + + final List incomeTransactions = new ArrayList<>(); + final List expenseTransactions = new ArrayList<>(); + final EnumMap> 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> 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 decorateAccounts(List accounts) { + return accounts.stream().map(AccountDecorator::new).collect(Collectors.toList()); + } } diff --git a/financer-web-client/src/main/java/de/financer/decorator/AccountDecorator.java b/financer-web-client/src/main/java/de/financer/decorator/AccountDecorator.java index c472399..a528c54 100644 --- a/financer-web-client/src/main/java/de/financer/decorator/AccountDecorator.java +++ b/financer-web-client/src/main/java/de/financer/decorator/AccountDecorator.java @@ -62,4 +62,8 @@ public class AccountDecorator { return null; } + + public Account getAccount() { + return account; + } } diff --git a/financer-web-client/src/main/java/de/financer/form/ConfigProjectedCashFlowForPeriodForm.java b/financer-web-client/src/main/java/de/financer/form/ConfigProjectedCashFlowForPeriodForm.java new file mode 100644 index 0000000..ddc9ebc --- /dev/null +++ b/financer-web-client/src/main/java/de/financer/form/ConfigProjectedCashFlowForPeriodForm.java @@ -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; + } +} diff --git a/financer-web-client/src/main/java/de/financer/form/ConfigProjectedCashFlowForPeriodTransactionsForm.java b/financer-web-client/src/main/java/de/financer/form/ConfigProjectedCashFlowForPeriodTransactionsForm.java new file mode 100644 index 0000000..6da0673 --- /dev/null +++ b/financer-web-client/src/main/java/de/financer/form/ConfigProjectedCashFlowForPeriodTransactionsForm.java @@ -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 incomeTransactions; + private List expenseTransactions; + private List 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 incomeTransactions, + List 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 getIncomeTransactions() { + return incomeTransactions; + } + + public void setIncomeTransactions(List incomeTransactions) { + this.incomeTransactions = incomeTransactions; + } + + public List getExpenseTransactions() { + return expenseTransactions; + } + + public void setExpenseTransactions(List expenseTransactions) { + this.expenseTransactions = expenseTransactions; + } + + public List getAccountContainers() { + return accountContainers; + } + + public void setAccountContainers(List 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; + } +} diff --git a/financer-web-client/src/main/java/de/financer/form/SelectReportForm.java b/financer-web-client/src/main/java/de/financer/form/SelectReportForm.java new file mode 100644 index 0000000..5f4fee1 --- /dev/null +++ b/financer-web-client/src/main/java/de/financer/form/SelectReportForm.java @@ -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; + } +} diff --git a/financer-web-client/src/main/java/de/financer/report/ReportType.java b/financer-web-client/src/main/java/de/financer/report/ReportType.java new file mode 100644 index 0000000..34ee25f --- /dev/null +++ b/financer-web-client/src/main/java/de/financer/report/ReportType.java @@ -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 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(); + } +} diff --git a/financer-web-client/src/main/java/de/financer/util/ControllerUtils.java b/financer-web-client/src/main/java/de/financer/util/ControllerUtils.java index 4a2eb67..86c6ad1 100644 --- a/financer-web-client/src/main/java/de/financer/util/ControllerUtils.java +++ b/financer-web-client/src/main/java/de/financer/util/ControllerUtils.java @@ -2,10 +2,7 @@ package de.financer.util; import de.financer.config.FinancerConfig; import de.financer.controller.Function; -import de.financer.model.Account; -import de.financer.model.AccountGroup; -import de.financer.model.AccountStatus; -import de.financer.model.RecurringTransaction; +import de.financer.model.*; import de.financer.util.comparator.AccountByTypeByGroupNameByIdComparator; import org.apache.commons.collections4.IterableUtils; import org.apache.commons.lang3.StringUtils; @@ -14,6 +11,7 @@ import org.springframework.ui.Model; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; +import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Locale; @@ -40,6 +38,15 @@ public class ControllerUtils { .collect(Collectors.toList()); } + public static List filterAndSortAccounts(Iterable 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 sortAccountGroups(Iterable accountGroups) { return IterableUtils.toList(accountGroups).stream() .sorted(Comparator.comparing(AccountGroup::getName)) diff --git a/financer-web-client/src/main/resources/i18n/message.properties b/financer-web-client/src/main/resources/i18n/message.properties index 76c4b71..21a0f32 100644 --- a/financer-web-client/src/main/resources/i18n/message.properties +++ b/financer-web-client/src/main/resources/i18n/message.properties @@ -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.create-account-group=Create new account group 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.recurring-transaction-calendar=Recurring transaction calendar 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.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.table-header.id=ID 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.create-upload-transactions=financer\: create uploaded transactions 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.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_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.calendar.calendarweek=CW 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.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_NAME=File name is missing! \ No newline at end of file +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 \ No newline at end of file diff --git a/financer-web-client/src/main/resources/i18n/message_de_DE.properties b/financer-web-client/src/main/resources/i18n/message_de_DE.properties index 847a42e..caa45be 100644 --- a/financer-web-client/src/main/resources/i18n/message_de_DE.properties +++ b/financer-web-client/src/main/resources/i18n/message_de_DE.properties @@ -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.create-recurring-transaction=Neue wiederkehrende Buchung erstellen 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-report=Einen Bericht erzeugen 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.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.type=Typ 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.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.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.submit=Konto-Gruppe erstellen +financer.account-group-new.submit=Kontogruppe erstellen financer.account-edit.title=financer\: Bearbeite Konto 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.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.to-date=Bis Datum\: 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.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.table-header.id=ID 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.recurring-transaction-new=financer\: Neue wiederkehrende Buchung 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-details=financer\: Kontodetails f\u00FCr {0} 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-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-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.recurring-transaction-calendar=financer\: Kalender wiederkehrende Buchung financer.heading.period-overview=financer\: Perioden\u00FCbersicht financer.heading.upload-transactions=financer\: Buchungen hochladen financer.heading.create-upload-transactions=financer\: Erstelle hochgeladene Buchungen 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.back-to-overview=Zur\u00FCck zur \u00DCbersicht financer.show-actions=Anzeigen... 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-for-period.title=Ausgaben in der Periode vom {0} bis {1} 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 Kontogruppe 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.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.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_FOR_PERIOD=Ausgaben f\u00FCr eine konfigurierbare 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 Kontogruppe (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.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.report.name.PROJECTED_CASH_FLOW=Prognostizierter Cashflow + financer.recurring-transaction-calendar.title=financer\: Kalender wiederkehrende Buchung financer.calendar.calendarweek=KW 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.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_GROUP_NAME=Eine Konto-Gruppe mit diesem Namen existiert bereits! -financer.error-message.ACCOUNT_GROUP_NOT_FOUND=Die ausgew\u00E4hlte Konto-Gruppe wurde nicht gefunden! +financer.error-message.DUPLICATE_ACCOUNT_GROUP_NAME=Eine Kontogruppe mit diesem Namen existiert bereits! +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.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_NAME=Der Dateiname fehlt! \ No newline at end of file +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 \ No newline at end of file diff --git a/financer-web-client/src/main/resources/static/changelog.txt b/financer-web-client/src/main/resources/static/changelog.txt index 089ddf0..0651608 100644 --- a/financer-web-client/src/main/resources/static/changelog.txt +++ b/financer-web-client/src/main/resources/static/changelog.txt @@ -1,5 +1,5 @@ v49 -> v50: -- +- #29 Added projected cash flow report v48 -> v49: - #27 The recurring transaction selection during transaction import now contains all recurring transaction instead of diff --git a/financer-web-client/src/main/resources/static/css/main.css b/financer-web-client/src/main/resources/static/css/main.css index dedaaf3..fef432b 100644 --- a/financer-web-client/src/main/resources/static/css/main.css +++ b/financer-web-client/src/main/resources/static/css/main.css @@ -72,7 +72,8 @@ a { #transaction-table, #recurring-transaction-list-table, #period-overview-table, -#create-upload-transactions-table { +#create-upload-transactions-table, +#report-config-projected-cashflow-for-period-transactions-table { width: 100%; border-collapse: collapse; text-align: left; @@ -89,7 +90,8 @@ a { #period-overview-table th, #period-overview-table td, #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); padding: 0.3em; vertical-align: top; @@ -174,6 +176,7 @@ tr:hover { #new-account-group-form *, #chart-config-account-group-expenses-for-period-form *, #chart-config-account-expenses-for-period-form *, +#report-config-projected-cashflow-for-period-form *, #search-transactions-form *, #upload-transactions-form *, #edit-account-form * { @@ -201,6 +204,12 @@ tr:hover { display: block; } +#report-select-form div { + width: 20em; + margin-top: 1em; + display: block; +} + input[type=submit] { margin-top: 1em; margin-bottom: 1em; @@ -397,4 +406,64 @@ input[type=submit] { .transaction-upload-fieldset * { 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; } \ No newline at end of file diff --git a/financer-web-client/src/main/resources/templates/account/accountOverview.html b/financer-web-client/src/main/resources/templates/account/accountOverview.html index a6a4a2d..0a0fe1e 100644 --- a/financer-web-client/src/main/resources/templates/account/accountOverview.html +++ b/financer-web-client/src/main/resources/templates/account/accountOverview.html @@ -73,6 +73,8 @@ diff --git a/financer-web-client/src/main/resources/templates/includes/footer.html b/financer-web-client/src/main/resources/templates/includes/footer.html index d62321f..5fcd894 100644 --- a/financer-web-client/src/main/resources/templates/includes/footer.html +++ b/financer-web-client/src/main/resources/templates/includes/footer.html @@ -1,4 +1,4 @@ -