Introduce periods (and other smaller stuff)

This commit is contained in:
2019-09-28 21:47:07 +02:00
parent f6cdec638e
commit e052a5a98a
58 changed files with 953 additions and 246 deletions

View File

@@ -3,6 +3,7 @@ package de.financer.chart.impl.expense;
import de.financer.config.FinancerConfig;
import de.financer.dto.AccountExpense;
import de.financer.dto.AccountGroupExpense;
import de.financer.template.GetAccountExpensesCurrentExpensePeriodTemplate;
import de.financer.template.GetAccountExpensesTemplate;
import de.financer.template.GetAccountGroupExpensesTemplate;
import org.apache.commons.collections4.IterableUtils;
@@ -12,8 +13,14 @@ import org.jfree.data.general.PieDataset;
public class AccountExpensesGenerator extends AbstractExpensesGenerator {
@Override
protected PieDataset getDataset(String start, String end, FinancerConfig financerConfig) {
final Iterable<AccountExpense> expenses = new GetAccountExpensesTemplate()
.exchange(financerConfig, start, end).getBody();
Iterable<AccountExpense> expenses;
if (start == null && end == null) {
expenses = new GetAccountExpensesCurrentExpensePeriodTemplate().exchange(financerConfig).getBody();
}
else {
expenses = new GetAccountExpensesTemplate().exchange(financerConfig, start, end).getBody();
}
final DefaultPieDataset dataSet = new DefaultPieDataset();

View File

@@ -2,6 +2,7 @@ package de.financer.chart.impl.expense;
import de.financer.config.FinancerConfig;
import de.financer.dto.AccountGroupExpense;
import de.financer.template.GetAccountGroupExpensesCurrentExpensePeriodTemplate;
import de.financer.template.GetAccountGroupExpensesTemplate;
import org.apache.commons.collections4.IterableUtils;
import org.jfree.data.general.DefaultPieDataset;
@@ -10,8 +11,14 @@ import org.jfree.data.general.PieDataset;
public class AccountGroupExpensesGenerator extends AbstractExpensesGenerator {
@Override
protected PieDataset getDataset(String start, String end, FinancerConfig financerConfig) {
final Iterable<AccountGroupExpense> expenses = new GetAccountGroupExpensesTemplate()
.exchange(financerConfig, start, end).getBody();
Iterable<AccountGroupExpense> expenses;
if (start == null && end == null) {
expenses = new GetAccountGroupExpensesCurrentExpensePeriodTemplate().exchange(financerConfig).getBody();
}
else {
expenses = new GetAccountGroupExpensesTemplate().exchange(financerConfig, start, end).getBody();
}
final DefaultPieDataset dataSet = new DefaultPieDataset();

View File

@@ -2,9 +2,8 @@ package de.financer.chart.impl.total;
import de.financer.chart.AbstractChartGenerator;
import de.financer.dto.ExpensePeriodTotal;
import de.financer.model.Period;
import de.financer.template.GetExpensePeriodTotalsTemplate;
import de.financer.util.ControllerUtils;
import de.financer.util.ExpensePeriod;
import org.apache.commons.collections4.IterableUtils;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
@@ -17,7 +16,7 @@ import org.springframework.context.i18n.LocaleContextHolder;
import java.text.NumberFormat;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.List;
import java.util.Comparator;
public class PeriodTotalGenerator extends AbstractChartGenerator<PeriodTotalParameter> {
@Override
@@ -45,30 +44,36 @@ public class PeriodTotalGenerator extends AbstractChartGenerator<PeriodTotalPara
private CategoryDataset getDataset(PeriodTotalParameter parameter) {
final DefaultCategoryDataset result = new DefaultCategoryDataset();
final List<ExpensePeriod> expensePeriods = ExpensePeriod
.generateExpensePeriodsForYear(this.getFinancerConfig().getMonthPeriodStartDay(), parameter.getYear());
final Iterable<ExpensePeriodTotal> totalData = new GetExpensePeriodTotalsTemplate()
.exchange(this.getFinancerConfig(), parameter.getYear()).getBody();
IterableUtils.toList(totalData).stream()
.sorted(Comparator.comparing((ExpensePeriodTotal ept) -> ept.getPeriod().getStart())
.thenComparing((ExpensePeriodTotal ept) -> ept.getType()))
.forEach((ept) -> result.addValue((ept.getTotal() / 100D),
this.getMessage("financer.account-type." + ept.getType()),
expensePeriods.stream()
.filter((ep) -> ep.generatePeriodShortCode()
.equals(ept.getPeriodShortCode()))
.map((ep) -> formatDateY(ep))
.findFirst().get()));
formatDateY(ept.getPeriod())));
return result;
}
private String formatDateY(ExpensePeriod ep) {
return String.format("%s - %s",
ep.getStart().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.withLocale(LocaleContextHolder.getLocale())),
ep.getEnd().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.withLocale(LocaleContextHolder.getLocale())));
private String formatDateY(Period ep) {
final String start = ep.getStart().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.withLocale(LocaleContextHolder.getLocale()));
String end;
if (ep.getEnd() == null) {
end = this.getMessage("financer.chart.expense-period-totals-current-year.open-period");
}
else {
end = ep.getEnd().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.withLocale(LocaleContextHolder.getLocale()));
}
return String.format("%s - %s", start, end);
}
}

View File

@@ -2,11 +2,11 @@ package de.financer.controller;
import de.financer.ResponseReason;
import de.financer.config.FinancerConfig;
import de.financer.decorator.AccountDecorator;
import de.financer.template.*;
import de.financer.form.NewAccountForm;
import de.financer.model.*;
import de.financer.util.ControllerUtils;
import de.financer.util.ExpensePeriod;
import de.financer.util.TransactionUtils;
import de.financer.util.comparator.TransactionByDateByIdDescComparator;
import org.apache.commons.collections4.IterableUtils;
@@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.List;
import java.util.stream.Collectors;
@Controller
public class AccountController {
@@ -34,18 +35,20 @@ public class AccountController {
final ResponseEntity<Iterable<RecurringTransaction>> rtAllActRes = new GetAllActiveRecurringTransactionsTemplate()
.exchange(this.financerConfig);
final ResponseEntity<Long> currentAssets = new GetCurrentAssetsTemplate().exchange(this.financerConfig);
final ResponseEntity<Long> currentExpenses = new GetExpensesCurrentPeriodTemplate().exchange(this.financerConfig);
final ResponseEntity<Long> currentExpenses = new GetExpensesCurrentPeriodTemplate()
.exchange(this.financerConfig);
final boolean showClosedBoolean = BooleanUtils.toBoolean(showClosed);
final ExpensePeriod expensePeriod = ExpensePeriod
.getCurrentExpensePeriod(this.financerConfig.getMonthPeriodStartDay());
final ResponseEntity<Period> expensePeriodRes = new GetCurrentExpensePeriodTemplate()
.exchange(this.financerConfig);
final Period expensePeriod = expensePeriodRes.getBody();
model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(response.getBody(), showClosedBoolean));
model.addAttribute("accounts", decorateAccounts(ControllerUtils
.filterAndSortAccounts(response.getBody(), showClosedBoolean)));
model.addAttribute("rtDueTodayCount", IterableUtils.size(rtDtRes.getBody()));
model.addAttribute("rtAllActiveCount", IterableUtils.size(rtAllActRes.getBody()));
model.addAttribute("currentAssets", currentAssets.getBody());
model.addAttribute("currentExpenses", currentExpenses.getBody());
model.addAttribute("periodStart", expensePeriod.getStart());
model.addAttribute("periodEnd", expensePeriod.getEnd());
model.addAttribute("showClosed", showClosedBoolean);
ControllerUtils.addVersionAttribute(model, this.financerConfig);
ControllerUtils.addCurrencySymbol(model, this.financerConfig);
@@ -53,6 +56,10 @@ public class AccountController {
return "account/accountOverview";
}
private Iterable<AccountDecorator> decorateAccounts(List<Account> accounts) {
return accounts.stream().map((a) -> new AccountDecorator(a)).collect(Collectors.toList());
}
@GetMapping("/newAccount")
public String newAccount(Model model) {
final ResponseEntity<Iterable<AccountGroup>> accountGroupResponse = new GetAllAccountGroupsTemplate()

View File

@@ -1,15 +1,14 @@
package de.financer.controller;
import de.financer.chart.impl.expense.ExpensesParameter;
import de.financer.chart.ChartGenerator;
import de.financer.chart.ChartType;
import de.financer.chart.FinancerChartFactory;
import de.financer.chart.impl.expense.ExpensesParameter;
import de.financer.chart.impl.total.PeriodTotalParameter;
import de.financer.config.FinancerConfig;
import de.financer.form.ConfigAccountExpenseForPeriodForm;
import de.financer.form.ConfigAccountGroupExpenseForPeriodForm;
import de.financer.util.ControllerUtils;
import de.financer.util.ExpensePeriod;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.springframework.beans.factory.annotation.Autowired;
@@ -32,12 +31,8 @@ public class ChartController {
@GetMapping("/getAccountGroupExpensesCurrentPeriod")
public void getAccountGroupExpensesCurrentPeriod(HttpServletResponse response) {
final ExpensePeriod period = ExpensePeriod
.getCurrentExpensePeriod(this.financerConfig.getMonthPeriodStartDay());
final ExpensesParameter parameter = new ExpensesParameter();
parameter.setPeriodStart(period.getStart());
parameter.setPeriodEnd(period.getEnd());
parameter.setTitle("financer.chart.account-group-expenses-current-period.title");
final ChartGenerator<ExpensesParameter> generator =
@@ -70,12 +65,8 @@ public class ChartController {
@GetMapping("/getAccountExpensesCurrentPeriod")
public void getAccountExpensesCurrentPeriod(HttpServletResponse response) {
final ExpensePeriod period = ExpensePeriod
.getCurrentExpensePeriod(this.financerConfig.getMonthPeriodStartDay());
final ExpensesParameter parameter = new ExpensesParameter();
parameter.setPeriodStart(period.getStart());
parameter.setPeriodEnd(period.getEnd());
parameter.setTitle("financer.chart.account-expenses-current-period.title");
final ChartGenerator<ExpensesParameter> generator =

View File

@@ -8,10 +8,12 @@ public enum Function {
ACC_OPEN_ACCOUNT("accounts/openAccount"),
ACC_CURRENT_ASSETS("accounts/getCurrentAssets"),
ACC_GET_ACC_EXPENSES("accounts/getAccountExpenses"),
ACC_GET_ACC_EXPENSES_CURRENT_EXPENSE_PERIOD("accounts/getAccountExpensesCurrentExpensePeriod"),
ACC_GP_CREATE_ACCOUNT_GROUP("accountGroups/createAccountGroup"),
ACC_GP_GET_ALL("accountGroups/getAll"),
ACC_GP_GET_ACC_GP_EXPENSES("accountGroups/getAccountGroupExpenses"),
ACC_GP_GET_ACC_GP_EXPENSES_CURRENT_EXPENSE_PERIOD("accountGroups/getAccountGroupExpensesCurrentExpensePeriod"),
TR_GET_ALL("transactions/getAll"),
TR_GET_ALL_FOR_ACCOUNT("transactions/getAllForAccount"),
@@ -26,7 +28,10 @@ public enum Function {
RT_GET_ALL_DUE_TODAY("recurringTransactions/getAllDueToday"),
RT_CREATE_RECURRING_TRANSACTION("recurringTransactions/createRecurringTransaction"),
RT_DELETE_RECURRING_TRANSACTION("recurringTransactions/deleteRecurringTransaction"),
RT_CREATE_TRANSACTION("recurringTransactions/createTransaction");
RT_CREATE_TRANSACTION("recurringTransactions/createTransaction"),
P_GET_CURRENT_EXPENSE_PERIOD("periods/getCurrentExpensePeriod"),
P_CLOSE_CURRENT_EXPENSE_PERIOD("periods/closeCurrentExpensePeriod");
private String path;

View File

@@ -0,0 +1,25 @@
package de.financer.controller;
import de.financer.config.FinancerConfig;
import de.financer.template.StringTemplate;
import de.financer.util.ControllerUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.util.UriComponentsBuilder;
@Controller
public class PeriodController {
@Autowired
private FinancerConfig financerConfig;
@GetMapping("/closePeriod")
public String closePeriod() {
final UriComponentsBuilder builder = UriComponentsBuilder
.fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.P_CLOSE_CURRENT_EXPENSE_PERIOD));
new StringTemplate().exchange(builder);
return "redirect:/accountOverview";
}
}

View File

@@ -2,13 +2,10 @@ package de.financer.controller;
import de.financer.ResponseReason;
import de.financer.config.FinancerConfig;
import de.financer.model.*;
import de.financer.template.*;
import de.financer.form.NewRecurringTransactionForm;
import de.financer.form.RecurringToTransactionWithAmountForm;
import de.financer.model.Account;
import de.financer.model.HolidayWeekendType;
import de.financer.model.IntervalType;
import de.financer.model.RecurringTransaction;
import de.financer.util.ControllerUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
@@ -19,6 +16,8 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Controller
public class RecurringTransactionController {
@@ -30,10 +29,18 @@ public class RecurringTransactionController {
public String newRecurringTransaction(Model model) {
final ResponseEntity<Iterable<Account>> response = new GetAllAccountsTemplate().exchange(this.financerConfig);
final NewRecurringTransactionForm form = new NewRecurringTransactionForm();
final List<Account> fromAccounts = ControllerUtils.filterAndSortAccounts(response.getBody()).stream()
.filter((a) -> a.getType() != AccountType.EXPENSE)
.collect(Collectors.toList());
final List<Account> toAccounts = ControllerUtils.filterAndSortAccounts(response.getBody()).stream()
.filter((a) -> a.getType() != AccountType.INCOME && a
.getType() != AccountType.START)
.collect(Collectors.toList());
form.setRemind(Boolean.TRUE);
model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(response.getBody()));
model.addAttribute("fromAccounts", fromAccounts);
model.addAttribute("toAccounts", toAccounts);
model.addAttribute("intervalTypes", IntervalType.valueList());
model.addAttribute("holidayWeekendTypes", HolidayWeekendType.valueList());
model.addAttribute("form", form);
@@ -64,8 +71,16 @@ public class RecurringTransactionController {
if (!ResponseReason.OK.equals(responseReason)) {
final ResponseEntity<Iterable<Account>> getAllResponse = new GetAllAccountsTemplate()
.exchange(this.financerConfig);
final List<Account> fromAccounts = ControllerUtils.filterAndSortAccounts(getAllResponse.getBody()).stream()
.filter((a) -> a.getType() != AccountType.EXPENSE)
.collect(Collectors.toList());
final List<Account> toAccounts = ControllerUtils.filterAndSortAccounts(getAllResponse.getBody()).stream()
.filter((a) -> a.getType() != AccountType.INCOME && a
.getType() != AccountType.START)
.collect(Collectors.toList());
model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(getAllResponse.getBody()));
model.addAttribute("fromAccounts", fromAccounts);
model.addAttribute("toAccounts", toAccounts);
model.addAttribute("intervalTypes", IntervalType.valueList());
model.addAttribute("holidayWeekendTypes", HolidayWeekendType.valueList());
model.addAttribute("form", form);

View File

@@ -2,6 +2,7 @@ package de.financer.controller;
import de.financer.ResponseReason;
import de.financer.config.FinancerConfig;
import de.financer.model.AccountType;
import de.financer.template.GetAccountByKeyTemplate;
import de.financer.template.GetAllAccountsTemplate;
import de.financer.template.GetAllTransactionsForAccountTemplate;
@@ -23,6 +24,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.List;
import java.util.stream.Collectors;
@Controller
public class TransactionController {
@@ -33,8 +35,16 @@ public class TransactionController {
@GetMapping("/newTransaction")
public String newTransaction(Model model) {
final ResponseEntity<Iterable<Account>> response = new GetAllAccountsTemplate().exchange(this.financerConfig);
final List<Account> fromAccounts = ControllerUtils.filterAndSortAccounts(response.getBody()).stream()
.filter((a) -> a.getType() != AccountType.EXPENSE)
.collect(Collectors.toList());
final List<Account> toAccounts = ControllerUtils.filterAndSortAccounts(response.getBody()).stream()
.filter((a) -> a.getType() != AccountType.INCOME && a
.getType() != AccountType.START)
.collect(Collectors.toList());
model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(response.getBody()));
model.addAttribute("fromAccounts", fromAccounts);
model.addAttribute("toAccounts", toAccounts);
model.addAttribute("form", new NewTransactionForm());
ControllerUtils.addVersionAttribute(model, this.financerConfig);
ControllerUtils.addCurrencySymbol(model, this.financerConfig);
@@ -57,8 +67,16 @@ public class TransactionController {
if (!ResponseReason.OK.equals(responseReason)) {
final ResponseEntity<Iterable<Account>> accRes = new GetAllAccountsTemplate().exchange(this.financerConfig);
final List<Account> fromAccounts = ControllerUtils.filterAndSortAccounts(accRes.getBody()).stream()
.filter((a) -> a.getType() != AccountType.EXPENSE)
.collect(Collectors.toList());
final List<Account> toAccounts = ControllerUtils.filterAndSortAccounts(accRes.getBody()).stream()
.filter((a) -> a.getType() != AccountType.INCOME && a
.getType() != AccountType.START)
.collect(Collectors.toList());
model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(accRes.getBody()));
model.addAttribute("fromAccounts", fromAccounts);
model.addAttribute("toAccounts", toAccounts);
model.addAttribute("form", form);
model.addAttribute("errorMessage", responseReason.name());
ControllerUtils.addVersionAttribute(model, this.financerConfig);
@@ -79,7 +97,8 @@ public class TransactionController {
final ResponseEntity<String> response = new StringTemplate().exchange(builder);
final ResponseReason responseReason = ResponseReason.fromResponseEntity(response);
final ResponseEntity<Account> accountResponse = new GetAccountByKeyTemplate().exchange(this.financerConfig, accountKey);
final ResponseEntity<Account> accountResponse = new GetAccountByKeyTemplate()
.exchange(this.financerConfig, accountKey);
final Account account = accountResponse.getBody();
final ResponseEntity<Iterable<Transaction>> transactionResponse = new GetAllTransactionsForAccountTemplate()
.exchange(this.financerConfig, account.getKey());

View File

@@ -0,0 +1,65 @@
package de.financer.decorator;
import de.financer.model.*;
public class AccountDecorator {
private final Account account;
public Long getId() {
return account.getId();
}
public String getKey() {
return account.getKey();
}
public AccountType getType() {
return account.getType();
}
public AccountStatus getStatus() {
return account.getStatus();
}
public Long getCurrentBalance() {
return account.getCurrentBalance();
}
public AccountGroup getAccountGroup() {
return account.getAccountGroup();
}
public AccountDecorator(Account account) {
this.account = account;
}
public Long getSpendingCurrentExpensePeriod() {
if (this.account.getType() == AccountType.EXPENSE || this.account.getType() == AccountType.LIABILITY) {
return this.account.getAccountStatistics().stream()
.filter((as) -> as.getPeriod().getType().equals(PeriodType.EXPENSE) && as.getPeriod()
.getEnd() == null)
.findFirst()
.orElseGet(() -> {
AccountStatistic as = new AccountStatistic();
as.setTransactionCountTo(0l);
return as;
})
.getSpendingTotalTo();
}
return null;
}
public Long getAverageSpendingExpensePeriod() {
if (this.account.getType() == AccountType.EXPENSE || this.account.getType() == AccountType.LIABILITY) {
return Math.round(this.account.getAccountStatistics().stream()
.filter((as) -> as.getPeriod().getType().equals(PeriodType.EXPENSE) && as.getPeriod()
.getEnd() != null)
.mapToLong((as) -> as.getSpendingTotalTo())
.average()
.orElseGet(() -> Double.valueOf(0d)));
}
return null;
}
}

View File

@@ -0,0 +1,20 @@
package de.financer.template;
import de.financer.config.FinancerConfig;
import de.financer.controller.Function;
import de.financer.dto.AccountExpense;
import de.financer.util.ControllerUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.ResponseEntity;
import org.springframework.web.util.UriComponentsBuilder;
public class GetAccountExpensesCurrentExpensePeriodTemplate {
public ResponseEntity<Iterable<AccountExpense>> exchange(FinancerConfig financerConfig) {
final UriComponentsBuilder expensesBuilder = UriComponentsBuilder
.fromHttpUrl(ControllerUtils.buildUrl(financerConfig, Function.ACC_GET_ACC_EXPENSES_CURRENT_EXPENSE_PERIOD));
return new FinancerRestTemplate<Iterable<AccountExpense>>()
.exchange(expensesBuilder.toUriString(), new ParameterizedTypeReference<Iterable<AccountExpense>>() {
});
}
}

View File

@@ -0,0 +1,20 @@
package de.financer.template;
import de.financer.config.FinancerConfig;
import de.financer.controller.Function;
import de.financer.dto.AccountGroupExpense;
import de.financer.util.ControllerUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.ResponseEntity;
import org.springframework.web.util.UriComponentsBuilder;
public class GetAccountGroupExpensesCurrentExpensePeriodTemplate {
public ResponseEntity<Iterable<AccountGroupExpense>> exchange(FinancerConfig financerConfig) {
final UriComponentsBuilder expensesBuilder = UriComponentsBuilder
.fromHttpUrl(ControllerUtils.buildUrl(financerConfig, Function.ACC_GP_GET_ACC_GP_EXPENSES_CURRENT_EXPENSE_PERIOD));
return new FinancerRestTemplate<Iterable<AccountGroupExpense>>()
.exchange(expensesBuilder.toUriString(), new ParameterizedTypeReference<Iterable<AccountGroupExpense>>() {
});
}
}

View File

@@ -0,0 +1,20 @@
package de.financer.template;
import de.financer.config.FinancerConfig;
import de.financer.controller.Function;
import de.financer.model.Period;
import de.financer.util.ControllerUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.ResponseEntity;
import org.springframework.web.util.UriComponentsBuilder;
public class GetCurrentExpensePeriodTemplate {
public ResponseEntity<Period> exchange(FinancerConfig financerConfig) {
final UriComponentsBuilder expensesBuilder = UriComponentsBuilder
.fromHttpUrl(ControllerUtils.buildUrl(financerConfig, Function.P_GET_CURRENT_EXPENSE_PERIOD));
return new FinancerRestTemplate<Period>()
.exchange(expensesBuilder.toUriString(), new ParameterizedTypeReference<Period>() {
});
}
}

View File

@@ -12,7 +12,6 @@ public class GetExpensePeriodTotalsTemplate {
public ResponseEntity<Iterable<ExpensePeriodTotal>> exchange(FinancerConfig financerConfig, int year) {
final UriComponentsBuilder builder = UriComponentsBuilder
.fromHttpUrl(ControllerUtils.buildUrl(financerConfig, Function.TR_EXPENSE_PERIOD_TOTALS))
.queryParam("monthPeriodStartDay", financerConfig.getMonthPeriodStartDay())
.queryParam("year", year);
return new FinancerRestTemplate<Iterable<ExpensePeriodTotal>>()

View File

@@ -10,8 +10,7 @@ import org.springframework.web.util.UriComponentsBuilder;
public class GetExpensesCurrentPeriodTemplate {
public ResponseEntity<Long> exchange(FinancerConfig financerConfig) {
final UriComponentsBuilder transactionBuilder = UriComponentsBuilder
.fromHttpUrl(ControllerUtils.buildUrl(financerConfig, Function.TR_EXPENSES_CURRENT_PERIOD))
.queryParam("monthPeriodStartDay", financerConfig.getMonthPeriodStartDay());
.fromHttpUrl(ControllerUtils.buildUrl(financerConfig, Function.TR_EXPENSES_CURRENT_PERIOD));
return new FinancerRestTemplate<Long>()
.exchange(transactionBuilder.toUriString(), new ParameterizedTypeReference<Long>() {

View File

@@ -44,6 +44,10 @@ public class ControllerUtils {
}
public static String formatDate(FinancerConfig financerConfig, LocalDate date) {
if (date == null) {
return null;
}
return date.format(DateTimeFormatter.ofPattern(financerConfig.getDateFormat()));
}

View File

@@ -8,6 +8,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.close-current-period=Close the current expense period
financer.account-overview.status=Status\:
financer.account-overview.status.recurring-transaction-due-today=Recurring transactions due today\:
financer.account-overview.status.recurring-transaction-active=Active recurring transactions\:
@@ -16,10 +17,12 @@ financer.account-overview.status.current-expenses=Expenses in the current period
financer.account-overview.table-header.id=ID
financer.account-overview.table-header.key=Account
financer.account-overview.table-header.group=Group
financer.account-overview.table-header.balance=Current Balance
financer.account-overview.table-header.balance=Balance
financer.account-overview.table-header.spending-current-period=Spending current period
financer.account-overview.table-header.average-spending-period=Average spending
financer.account-overview.table-header.type=Type
financer.account-overview.table-header.status=Status
financer.account-overview.tooltip.status.current-expenses=Period from {0} to {1}. Clicking the amount will open a graphical overview about the expenses grouped by account group
financer.account-overview.tooltip.status.current-expenses=Period starting at {0}. Clicking the amount will open a graphical overview about the expenses grouped by account group
financer.account-overview.tooltip.status.current-assets=Assets available at short-notice
financer.account-new.title=financer\: create new account
@@ -88,6 +91,7 @@ financer.account-details.available-actions=Available actions\:
financer.account-details.available-actions.close-account=Close account
financer.account-details.available-actions.open-account=Open account
financer.account-details.available-actions.back-to-overview=Back to overview
financer.account-details.available-actions.create-transaction=Create new transaction
financer.account-details.table-header.id=ID
financer.account-details.table-header.fromAccount=From account
financer.account-details.table-header.toAccount=To account
@@ -163,6 +167,7 @@ financer.chart.account-expenses-for-period.title=Expenses for period from {0} to
financer.chart.expense-period-totals-current-year.title=Expense period totals for the current year
financer.chart.expense-period-totals-current-year.x=Amount
financer.chart.expense-period-totals-current-year.y=Period
financer.chart.expense-period-totals-current-year.open-period=OPEN
financer.chart.name.ACCOUNT_GROUP_EXPENSES_CURRENT_PERIOD=Expenses of the current period grouped by account group (pie chart)
financer.chart.name.ACCOUNT_GROUP_EXPENSES_FOR_PERIOD=Expenses for a configurable period grouped by account group (pie chart)

View File

@@ -8,6 +8,7 @@ financer.account-overview.available-actions.create-recurring-transaction=Neue wi
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.select-chart=Ein Diagramm erzeugen
financer.account-overview.available-actions.close-current-period=Aktuelle Periode schlie\u00DFen
financer.account-overview.status=Status\:
financer.account-overview.status.recurring-transaction-due-today=Wiederkehrende Buchungen heute f\u00E4llig\:
financer.account-overview.status.recurring-transaction-active=Aktive wiederkehrende Buchungen\:
@@ -19,7 +20,7 @@ financer.account-overview.table-header.group=Gruppe
financer.account-overview.table-header.balance=Kontostand
financer.account-overview.table-header.type=Typ
financer.account-overview.table-header.status=Status
financer.account-overview.tooltip.status.current-expenses=Periode ab {0} bis {1}. 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 Konto-Gruppe angezeigt werden
financer.account-overview.tooltip.status.current-assets=Kurzfristig verf\u00FCgbares Verm\u00F6gen
financer.account-new.title=financer\: Neues Konto erstellen
@@ -88,6 +89,7 @@ financer.account-details.available-actions=Verf\u00FCgbare Aktionen\:
financer.account-details.available-actions.close-account=Konto schlie\u00DFen
financer.account-details.available-actions.open-account=Konto \u00F6ffnen
financer.account-details.available-actions.back-to-overview=Zur\u00FCck zur \u00DCbersicht
financer.account-details.available-actions.create-transaction=Neue Buchung erstellen
financer.account-details.table-header.id=ID
financer.account-details.table-header.fromAccount=Von Konto
financer.account-details.table-header.toAccount=An Konto
@@ -162,6 +164,7 @@ financer.chart.account-expenses-for-period.title=Ausgaben in der Periode vom {0}
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.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)

View File

@@ -1,3 +1,15 @@
v18 -> v19:
- Fix a bug in recurring transaction handling that caused a recurring transaction with Holiday/Weekend type
'previous workday' and a planned occurrence on a Sunday to be due on Friday and Saturday
- Filter from and to account lists when creating a transaction or a recurring transaction by account types that
are allowed in from and to
- Add the create new transaction action to the account detail view
- Rework periods (e.g. expense) so that the underlying technical structures are better defined. This is preparation for
more reports and budgeting functions
- Add action to close the current expense period manually. This also opens a new one
- Add spending in current expense period per account to the account overview
- Add average spending in expense period per account to the account overview
v17 -> v18:
- Add readme to the footer
- Translate error messages to German

View File

@@ -18,9 +18,9 @@
<span th:text="#{financer.account-details.details.balance}"/>
<span th:text="${#numbers.formatDecimal(account.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
</div>
<div id="group-container">
<div id="group-container" th:if="${account.accountGroup != null}">
<span th:text="#{financer.account-details.details.group}"/>
<span th:text="${account.accountGroup?.name}"/>
<span th:text="${account.accountGroup.name}"/>
</div>
</div>
<div id="account-details-action-container">
@@ -29,6 +29,8 @@
th:text="#{financer.account-details.available-actions.close-account}"/>
<a th:if="${isClosed}" th:href="@{/openAccount(key=${account.key})}"
th:text="#{financer.account-details.available-actions.open-account}"/>
<a th:href="@{/newTransaction}"
th:text="#{financer.account-details.available-actions.create-transaction}"/>
<a th:href="@{/accountOverview}"
th:text="#{financer.account-details.available-actions.back-to-overview}"/>
</div>

View File

@@ -8,13 +8,14 @@
</head>
<body>
<h1 th:text="#{financer.heading.account-overview}" />
<span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/>
<div id="status-container">
<span th:text="#{financer.account-overview.status}"/>
<div th:title="#{financer.account-overview.tooltip.status.current-assets}">
<span th:text="#{financer.account-overview.status.current-assets}"/>
<span th:text="${#numbers.formatDecimal(currentAssets/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
</div>
<div th:title="#{'financer.account-overview.tooltip.status.current-expenses'(${#temporals.format(periodStart)}, ${#temporals.format(periodEnd)})}">
<div th:title="#{'financer.account-overview.tooltip.status.current-expenses'(${#temporals.format(periodStart)})}">
<span th:text="#{financer.account-overview.status.current-expenses}"/>
<a th:href="@{/getAccountGroupExpensesCurrentPeriod}">
<span th:text="${#numbers.formatDecimal(currentExpenses/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}" />
@@ -28,7 +29,6 @@
<span th:text="#{financer.account-overview.status.recurring-transaction-active}"/>
<a th:href="@{/recurringTransactionActive}" th:text="${rtAllActiveCount}"/>
</div>
</div>
<span th:text="#{financer.account-overview.available-actions}"/>
<div id="action-container">
@@ -49,6 +49,10 @@
<a th:href="@{/recurringTransactionAll}"
th:text="#{financer.account-overview.available-actions.recurring-transaction-all}"/>
</div>
<div id="action-container-sub-period">
<a th:href="@{/closePeriod}"
th:text="#{financer.account-overview.available-actions.close-current-period}"/>
</div>
<div id="action-container-sub-reports">
<a th:href="@{/selectChart}"
th:text="#{financer.account-overview.available-actions.select-chart}"/>
@@ -59,6 +63,8 @@
<th class="hideable-column" th:text="#{financer.account-overview.table-header.id}"/>
<th th:text="#{financer.account-overview.table-header.key}"/>
<th th:text="#{financer.account-overview.table-header.balance}"/>
<th class="hideable-column" th:text="#{financer.account-overview.table-header.spending-current-period}"/>
<th class="hideable-column" th:text="#{financer.account-overview.table-header.average-spending-period}"/>
<th class="hideable-column" th:text="#{financer.account-overview.table-header.group}"/>
<th class="hideable-column" th:text="#{financer.account-overview.table-header.type}"/>
<th class="hideable-column" th:text="#{financer.account-overview.table-header.status}"/>
@@ -69,6 +75,12 @@
<a th:href="@{/accountDetails(key=${acc.key})}" th:text="${acc.key}"/>
</td>
<td th:text="${#numbers.formatDecimal(acc.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
<td th:if="${acc.spendingCurrentExpensePeriod != null}"
th:text="${#numbers.formatDecimal(acc.spendingCurrentExpensePeriod/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
<td th:if="${acc.averageSpendingExpensePeriod != null}"
th:text="${#numbers.formatDecimal(acc.averageSpendingExpensePeriod/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
<td th:if="${acc.spendingCurrentExpensePeriod == null}">-</td>
<td th:if="${acc.averageSpendingExpensePeriod == null}">-</td>
<td class="hideable-column" th:text="${acc.accountGroup?.name}"/>
<td class="hideable-column" th:text="#{'financer.account-type.' + ${acc.type}}"/>
<td class="hideable-column" th:text="#{'financer.account-status.' + ${acc.status}}"/>

View File

@@ -14,12 +14,12 @@
method="post">
<label for="selectFromAccount" th:text="#{financer.recurring-transaction-new.label.from-account}"/>
<select id="selectFromAccount" th:field="*{fromAccountKey}">
<option th:each="acc : ${accounts}" th:value="${acc.key}"
<option th:each="acc : ${fromAccounts}" th:value="${acc.key}"
th:text="#{'financer.recurring-transaction-new.account-type.' + ${acc.type}(${acc.key},${#numbers.formatDecimal(acc.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT')},${currencySymbol})}"/>
</select>
<label for="selectToAccount" th:text="#{financer.recurring-transaction-new.label.to-account}"/>
<select id="selectToAccount" th:field="*{toAccountKey}">
<option th:each="acc : ${accounts}" th:value="${acc.key}"
<option th:each="acc : ${toAccounts}" th:value="${acc.key}"
th:text="#{'financer.recurring-transaction-new.account-type.' + ${acc.type}(${acc.key},${#numbers.formatDecimal(acc.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT')},${currencySymbol})}"/>
</select>
<label for="inputAmount" th:text="#{financer.recurring-transaction-new.label.amount}"/>

View File

@@ -14,12 +14,12 @@
method="post">
<label for="selectFromAccount" th:text="#{financer.transaction-new.label.from-account}"/>
<select id="selectFromAccount" th:field="*{fromAccountKey}">
<option th:each="acc : ${accounts}" th:value="${acc.key}"
<option th:each="acc : ${fromAccounts}" th:value="${acc.key}"
th:text="#{'financer.transaction-new.account-type.' + ${acc.type}(${acc.key},${#numbers.formatDecimal(acc.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT')},${currencySymbol})}"/>
</select>
<label for="selectToAccount" th:text="#{financer.transaction-new.label.to-account}"/>
<select id="selectToAccount" th:field="*{toAccountKey}">
<option th:each="acc : ${accounts}" th:value="${acc.key}"
<option th:each="acc : ${toAccounts}" th:value="${acc.key}"
th:text="#{'financer.transaction-new.account-type.' + ${acc.type}(${acc.key},${#numbers.formatDecimal(acc.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT')},${currencySymbol})}"/>
</select>
<label for="inputAmount" th:text="#{financer.transaction-new.label.amount}"/>