diff --git a/financer-common/pom.xml b/financer-common/pom.xml index 97e61b4..eb5cbe2 100644 --- a/financer-common/pom.xml +++ b/financer-common/pom.xml @@ -20,6 +20,14 @@ javax.persistence javax.persistence-api + + com.google.guava + guava + + + org.apache.commons + commons-lang3 + diff --git a/financer-common/src/main/java/de/financer/dto/ExpensePeriodTotal.java b/financer-common/src/main/java/de/financer/dto/ExpensePeriodTotal.java new file mode 100644 index 0000000..a18c060 --- /dev/null +++ b/financer-common/src/main/java/de/financer/dto/ExpensePeriodTotal.java @@ -0,0 +1,43 @@ +package de.financer.dto; + +import de.financer.model.AccountType; + +public class ExpensePeriodTotal { + private Long total; + private String periodShortCode; + private AccountType type; + + public ExpensePeriodTotal() { + // nothing to do + } + + public ExpensePeriodTotal(String periodShortCode, Long total, AccountType type) { + this.total = total; + this.periodShortCode = periodShortCode; + this.type = type; + } + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + + public String getPeriodShortCode() { + return periodShortCode; + } + + public void setPeriodShortCode(String periodShortCode) { + this.periodShortCode = periodShortCode; + } + + public AccountType getType() { + return type; + } + + public void setType(AccountType type) { + this.type = type; + } +} diff --git a/financer-common/src/main/java/de/financer/util/ExpensePeriod.java b/financer-common/src/main/java/de/financer/util/ExpensePeriod.java index db0fd13..4379bb7 100644 --- a/financer-common/src/main/java/de/financer/util/ExpensePeriod.java +++ b/financer-common/src/main/java/de/financer/util/ExpensePeriod.java @@ -1,6 +1,13 @@ package de.financer.util; +import com.google.common.collect.Streams; + import java.time.LocalDate; +import java.time.Month; +import java.time.Period; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class ExpensePeriod { private LocalDate start; @@ -24,27 +31,65 @@ public class ExpensePeriod { // => 10 < 15 // now - one month = 2019-05-10 // set the day = 2019-05-15 = start - // end = start + one month = 2019-06-15 - // Period from 2019-05-15 to 2019-06-15 + // end = start + one month - one day = 2019-06-14 + // Period from 2019-05-15 to 2019-06-14 periodStart = LocalDate.now().minusMonths(1).withDayOfMonth(periodStartDay); - periodEnd = periodStart.plusMonths(1); - } - else { + periodEnd = periodStart.plusMonths(1).minusDays(1); + } else { // Else, the current day of month is greater or equals the configured period // start day, just reset the day of month to the configured day, for example: // configured start day of month for period = 15 // now = 2019-06-26 // => 26 > 15 // set the day = 2019-06-15 = start - // end = start + one month = 2019-07-15 - // Period from 2019-06-15 to 2019-07-15 + // end = start + one month - one day = 2019-07-14 + // Period from 2019-06-15 to 2019-07-14 periodStart = LocalDate.now().withDayOfMonth(periodStartDay); - periodEnd = periodStart.plusMonths(1); + periodEnd = periodStart.plusMonths(1).minusDays(1); } return new ExpensePeriod(periodStart, periodEnd); } + public static final List generateExpensePeriodsForYear(int periodStartDay, int year) { + Stream localDateStreamStart; + Stream localDateStreamEnd; + + if (periodStartDay == 1) { + localDateStreamStart = LocalDate.of(year, Month.JANUARY, 1) + .datesUntil(LocalDate.of(year, Month.DECEMBER, 2), Period + .ofMonths(1)); + + localDateStreamEnd = LocalDate.of(year, Month.JANUARY, 31) + .datesUntil(LocalDate.of(year + 1, Month.JANUARY, 1), Period + .ofMonths(1)); + } + // If the start of the period is not the 1st we need to look to the previous year as well + // because the period in January actually started in December last year + else { + localDateStreamStart = LocalDate.of(year, Month.JANUARY, periodStartDay).minusMonths(1) + .datesUntil(LocalDate.of(year, Month.DECEMBER, periodStartDay + 1).plusDays(1), Period + .ofMonths(1)); + + localDateStreamEnd = LocalDate.of(year, Month.JANUARY, periodStartDay - 1) + .datesUntil(LocalDate.of(year, Month.DECEMBER, periodStartDay + 1).plusDays(1), Period + .ofMonths(1)); + } + + return Streams + .zip(localDateStreamStart, localDateStreamEnd, (start, end) -> new ExpensePeriod(start, end)) + .collect(Collectors.toList()); + } + + public String generatePeriodShortCode() { + return String.format("%s/%s", this.start.getYear(), this.start.getMonthValue()); + } + + @Override + public String toString() { + return String.format("Start[%s], End[%s], ShortCode[%s]", this.start, this.end, this.generatePeriodShortCode()); + } + public LocalDate getStart() { return start; } diff --git a/financer-server/src/main/java/de/financer/controller/TransactionController.java b/financer-server/src/main/java/de/financer/controller/TransactionController.java index e8180c9..b5e15aa 100644 --- a/financer-server/src/main/java/de/financer/controller/TransactionController.java +++ b/financer-server/src/main/java/de/financer/controller/TransactionController.java @@ -1,6 +1,7 @@ package de.financer.controller; import de.financer.ResponseReason; +import de.financer.dto.ExpensePeriodTotal; import de.financer.model.Transaction; import de.financer.service.TransactionService; import org.slf4j.Logger; @@ -10,9 +11,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; - @RestController @RequestMapping("transactions") public class TransactionController { @@ -94,4 +92,20 @@ public class TransactionController { return response; } + + @RequestMapping("getExpensePeriodTotals") + public Iterable getExpensePeriodTotals(Integer monthPeriodStartDay, Integer year) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String.format("/transactions/getExpensePeriodTotals got parameters: %s, %s", monthPeriodStartDay, year)); + } + + final Iterable expensePeriodTotals = this.transactionService + .getExpensePeriodTotals(monthPeriodStartDay, year); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String.format("/transactions/getExpensePeriodTotals returns with %s", expensePeriodTotals)); + } + + return expensePeriodTotals; + } } diff --git a/financer-server/src/main/java/de/financer/dba/TransactionRepository.java b/financer-server/src/main/java/de/financer/dba/TransactionRepository.java index 1fb6f7c..5691f2d 100644 --- a/financer-server/src/main/java/de/financer/dba/TransactionRepository.java +++ b/financer-server/src/main/java/de/financer/dba/TransactionRepository.java @@ -1,5 +1,6 @@ package de.financer.dba; +import de.financer.dto.ExpensePeriodTotal; import de.financer.model.Account; import de.financer.model.AccountType; import de.financer.model.Transaction; @@ -9,6 +10,7 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; +import java.util.List; @Transactional(propagation = Propagation.REQUIRED) public interface TransactionRepository extends CrudRepository { @@ -16,4 +18,11 @@ public interface TransactionRepository extends CrudRepository @Query("SELECT SUM(t.amount) FROM Transaction t JOIN t.toAccount a WHERE t.date BETWEEN :periodStart AND :periodEnd AND a.type IN :expenseTypes") Long getExpensesCurrentPeriod(LocalDate periodStart, LocalDate periodEnd, AccountType... expenseTypes); + + // The HQL contains a hack because Hibernate can't resolve the alias of the CASE column in the GROUP BY clause + // That's why the generated alias is used directly in the HQL. It will break if the columns in the SELECT clause get reordered + // col_0_0_ instead of periodShortCode + // col_2_0_ instead of AccType + @Query("SELECT new de.financer.dto.ExpensePeriodTotal(CASE WHEN EXTRACT(DAY FROM t.date) >= :startDay THEN CONCAT(CAST(EXTRACT(YEAR FROM t.date) AS string), '/', CAST(EXTRACT(MONTH FROM t.date) AS string)) ELSE CASE WHEN EXTRACT(MONTH FROM t.date) = 1 THEN CONCAT(CAST(EXTRACT(YEAR FROM t.date) - 1 AS string), '/12') ELSE CONCAT(CAST(EXTRACT(YEAR FROM t.date) AS string), '/', CAST(EXTRACT(MONTH FROM t.date) - 1 AS string)) END END AS periodShortCode, SUM(t.amount), CASE WHEN fa.type = :incomeType THEN fa.type ELSE ta.type END AS AccType) FROM Transaction t JOIN t.toAccount ta JOIN t.fromAccount fa WHERE ((ta.type IN :expenseTypes AND fa.type <> :startType) OR (fa.type = :incomeType)) AND t.date BETWEEN :lowerBound AND :upperBound GROUP BY col_0_0_, col_2_0_ ORDER BY periodShortCode, AccType ASC") + List getAccountExpenseTotals(int startDay, LocalDate lowerBound, LocalDate upperBound, AccountType incomeType, AccountType startType, AccountType... expenseTypes); } \ No newline at end of file diff --git a/financer-server/src/main/java/de/financer/service/TransactionService.java b/financer-server/src/main/java/de/financer/service/TransactionService.java index d9efca3..721ee71 100644 --- a/financer-server/src/main/java/de/financer/service/TransactionService.java +++ b/financer-server/src/main/java/de/financer/service/TransactionService.java @@ -1,8 +1,10 @@ package de.financer.service; +import com.google.common.collect.Iterables; import de.financer.ResponseReason; import de.financer.config.FinancerConfig; import de.financer.dba.TransactionRepository; +import de.financer.dto.ExpensePeriodTotal; import de.financer.model.Account; import de.financer.model.AccountType; import de.financer.model.RecurringTransaction; @@ -21,6 +23,7 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.Collections; +import java.util.List; import java.util.Optional; @Service @@ -239,4 +242,17 @@ public class TransactionService { return Optional.ofNullable(expensesCurrentPeriod).orElse(Long.valueOf(0l)); } + + public Iterable getExpensePeriodTotals(Integer monthPeriodStartDay, Integer year) { + final List expensePeriods = ExpensePeriod.generateExpensePeriodsForYear(monthPeriodStartDay, year); + + final ExpensePeriod lowerBound = Iterables.get(expensePeriods, 0); + final ExpensePeriod upperBound = Iterables.getLast(expensePeriods); + + final List expensePeriodTotals = this.transactionRepository + .getAccountExpenseTotals(monthPeriodStartDay, lowerBound.getStart(), upperBound + .getEnd(), AccountType.INCOME, AccountType.START, AccountType.EXPENSE, AccountType.LIABILITY); + + return expensePeriodTotals; + } } diff --git a/financer-web-client/src/main/java/de/financer/chart/ChartType.java b/financer-web-client/src/main/java/de/financer/chart/ChartType.java index 96e813c..c89857e 100644 --- a/financer-web-client/src/main/java/de/financer/chart/ChartType.java +++ b/financer-web-client/src/main/java/de/financer/chart/ChartType.java @@ -8,7 +8,8 @@ public enum ChartType { ACCOUNT_GROUP_EXPENSES_CURRENT_PERIOD, ACCOUNT_GROUP_EXPENSES_FOR_PERIOD, ACCOUNT_EXPENSES_CURRENT_PERIOD, - ACCOUNT_EXPENSES_FOR_PERIOD; + ACCOUNT_EXPENSES_FOR_PERIOD, + EXPENSE_PERIOD_TOTALS_CURRENT_YEAR; public static List valueList() { return Arrays.stream(ChartType.values()).map(ChartType::name).collect(Collectors.toList()); diff --git a/financer-web-client/src/main/java/de/financer/chart/FinancerChartFactory.java b/financer-web-client/src/main/java/de/financer/chart/FinancerChartFactory.java index 718a2f4..924b442 100644 --- a/financer-web-client/src/main/java/de/financer/chart/FinancerChartFactory.java +++ b/financer-web-client/src/main/java/de/financer/chart/FinancerChartFactory.java @@ -2,6 +2,7 @@ package de.financer.chart; import de.financer.chart.impl.expense.AccountExpensesGenerator; import de.financer.chart.impl.expense.AccountGroupExpensesGenerator; +import de.financer.chart.impl.total.PeriodTotalGenerator; import de.financer.config.FinancerConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; @@ -31,6 +32,10 @@ public class FinancerChartFactory { // TODO WHY IS THIS CAST NECESSARY??? generator = (AbstractChartGenerator

) new AccountExpensesGenerator(); break; + case EXPENSE_PERIOD_TOTALS_CURRENT_YEAR: + // TODO WHY IS THIS CAST NECESSARY??? + generator = (AbstractChartGenerator

) new PeriodTotalGenerator(); + break; default: generator = null; } diff --git a/financer-web-client/src/main/java/de/financer/chart/impl/total/PeriodTotalGenerator.java b/financer-web-client/src/main/java/de/financer/chart/impl/total/PeriodTotalGenerator.java new file mode 100644 index 0000000..e65bb7c --- /dev/null +++ b/financer-web-client/src/main/java/de/financer/chart/impl/total/PeriodTotalGenerator.java @@ -0,0 +1,74 @@ +package de.financer.chart.impl.total; + +import de.financer.chart.AbstractChartGenerator; +import de.financer.dto.ExpensePeriodTotal; +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; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.data.category.CategoryDataset; +import org.jfree.data.category.DefaultCategoryDataset; +import org.springframework.context.i18n.LocaleContextHolder; + +import java.text.NumberFormat; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.List; + +public class PeriodTotalGenerator extends AbstractChartGenerator { + @Override + public JFreeChart generateChart(PeriodTotalParameter parameter) { + final CategoryDataset dataSet = getDataset(parameter); + + final JFreeChart chart = ChartFactory + .createBarChart(this.getMessage(parameter.getTitle(), parameter.getArgsForTitle()), + this.getMessage(parameter.getyAxis()), + this.getMessage(parameter.getxAxis()), + dataSet, PlotOrientation.VERTICAL, true, true, false); + + final NumberAxis axis = (NumberAxis) chart.getCategoryPlot().getRangeAxis(); + + axis.setStandardTickUnits(NumberAxis.createIntegerTickUnits(LocaleContextHolder + .getLocale())); + + final NumberFormat currencyInstance = NumberFormat.getCurrencyInstance(LocaleContextHolder.getLocale()); + currencyInstance.setCurrency(this.getFinancerConfig().getCurrency()); + + axis.setNumberFormatOverride(currencyInstance); + + return chart; + } + + private CategoryDataset getDataset(PeriodTotalParameter parameter) { + final DefaultCategoryDataset result = new DefaultCategoryDataset(); + final List expensePeriods = ExpensePeriod + .generateExpensePeriodsForYear(this.getFinancerConfig().getMonthPeriodStartDay(), parameter.getYear()); + + final Iterable totalData = new GetExpensePeriodTotalsTemplate() + .exchange(this.getFinancerConfig(), parameter.getYear()).getBody(); + + IterableUtils.toList(totalData).stream() + .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())); + + + 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()))); + } +} diff --git a/financer-web-client/src/main/java/de/financer/chart/impl/total/PeriodTotalParameter.java b/financer-web-client/src/main/java/de/financer/chart/impl/total/PeriodTotalParameter.java new file mode 100644 index 0000000..97f97cb --- /dev/null +++ b/financer-web-client/src/main/java/de/financer/chart/impl/total/PeriodTotalParameter.java @@ -0,0 +1,51 @@ +package de.financer.chart.impl.total; + +import de.financer.chart.ChartParameter; + +public class PeriodTotalParameter implements ChartParameter { + private String title; + private Object[] argsForTitle; + private String yAxis; + private String xAxis; + private int year; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Object[] getArgsForTitle() { + return argsForTitle; + } + + public void setArgsForTitle(Object[] argsForTitle) { + this.argsForTitle = argsForTitle; + } + + public int getYear() { + return year; + } + + public void setYear(int year) { + this.year = year; + } + + public String getyAxis() { + return yAxis; + } + + public void setyAxis(String yAxis) { + this.yAxis = yAxis; + } + + public String getxAxis() { + return xAxis; + } + + public void setxAxis(String xAxis) { + this.xAxis = xAxis; + } +} diff --git a/financer-web-client/src/main/java/de/financer/controller/ChartController.java b/financer-web-client/src/main/java/de/financer/controller/ChartController.java index 81b7836..4802bc4 100644 --- a/financer-web-client/src/main/java/de/financer/controller/ChartController.java +++ b/financer-web-client/src/main/java/de/financer/controller/ChartController.java @@ -4,6 +4,7 @@ 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.total.PeriodTotalParameter; import de.financer.config.FinancerConfig; import de.financer.form.ConfigAccountExpenseForPeriodForm; import de.financer.form.ConfigAccountGroupExpenseForPeriodForm; @@ -19,6 +20,7 @@ import org.springframework.web.bind.annotation.PostMapping; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.time.LocalDate; @Controller public class ChartController { @@ -104,6 +106,23 @@ public class ChartController { writeChart(response, chart); } + @GetMapping("/getExpensePeriodTotalCurrentYear") + public void getExpensePeriodTotalCurrentYear(HttpServletResponse response) { + PeriodTotalParameter parameter = new PeriodTotalParameter(); + + parameter.setYear(LocalDate.now().getYear()); + parameter.setTitle("financer.chart.expense-period-totals-current-year.title"); + parameter.setxAxis("financer.chart.expense-period-totals-current-year.x"); + parameter.setyAxis("financer.chart.expense-period-totals-current-year.y"); + + final ChartGenerator generator = + this.financerChartFactory.getGenerator(ChartType.EXPENSE_PERIOD_TOTALS_CURRENT_YEAR); + + final JFreeChart chart = generator.generateChart(parameter); + + writeChart(response, chart); + } + private void writeChart(HttpServletResponse response, JFreeChart chart) { response.setContentType("image/png"); diff --git a/financer-web-client/src/main/java/de/financer/controller/Function.java b/financer-web-client/src/main/java/de/financer/controller/Function.java index aa4786a..899fbc9 100644 --- a/financer-web-client/src/main/java/de/financer/controller/Function.java +++ b/financer-web-client/src/main/java/de/financer/controller/Function.java @@ -18,6 +18,7 @@ public enum Function { TR_CREATE_TRANSACTION("transactions/createTransaction"), TR_DELETE_TRANSACTION("transactions/deleteTransaction"), TR_EXPENSES_CURRENT_PERIOD("transactions/getExpensesCurrentPeriod"), + TR_EXPENSE_PERIOD_TOTALS("transactions/getExpensePeriodTotals"), RT_GET_ALL("recurringTransactions/getAll"), RT_GET_ALL_ACTIVE("recurringTransactions/getAllActive"), 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 ace67d9..8546bc3 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 @@ -67,6 +67,10 @@ public class ReportController { ControllerUtils.addCurrencySymbol(model, this.financerConfig); return "report/configureAccountExpensesForPeriod"; + case EXPENSE_PERIOD_TOTALS_CURRENT_YEAR: + // Special case: this chart does not require parameters, so we can + // directly redirect to the actual chart instead of the config page + return "redirect:/getExpensePeriodTotalCurrentYear"; default: // Cannot happen throw new IllegalStateException("Unexpected value: " + selectedChartType); diff --git a/financer-web-client/src/main/java/de/financer/template/GetExpensePeriodTotalsTemplate.java b/financer-web-client/src/main/java/de/financer/template/GetExpensePeriodTotalsTemplate.java new file mode 100644 index 0000000..3c4795f --- /dev/null +++ b/financer-web-client/src/main/java/de/financer/template/GetExpensePeriodTotalsTemplate.java @@ -0,0 +1,22 @@ +package de.financer.template; + +import de.financer.config.FinancerConfig; +import de.financer.controller.Function; +import de.financer.dto.ExpensePeriodTotal; +import de.financer.util.ControllerUtils; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.ResponseEntity; +import org.springframework.web.util.UriComponentsBuilder; + +public class GetExpensePeriodTotalsTemplate { + public ResponseEntity> 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>() + .exchange(builder.toUriString(), new ParameterizedTypeReference>() { + }); + } +} diff --git a/financer-web-client/src/main/resources/i18n/message.properties b/financer-web-client/src/main/resources/i18n/message.properties index 88f75f8..6a00e8b 100644 --- a/financer-web-client/src/main/resources/i18n/message.properties +++ b/financer-web-client/src/main/resources/i18n/message.properties @@ -160,11 +160,16 @@ financer.chart.account-group-expenses-current-period.title=Expenses of the curre financer.chart.account-group-expenses-for-period.title=Expenses for period from {0} to {1} grouped by account group financer.chart.account-expenses-current-period.title=Expenses of the current period grouped by account financer.chart.account-expenses-for-period.title=Expenses for period from {0} to {1} grouped by account +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.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) financer.chart.name.ACCOUNT_EXPENSES_CURRENT_PERIOD=Expenses of the current period grouped by account (pie chart) financer.chart.name.ACCOUNT_EXPENSES_FOR_PERIOD=Expenses for a configurable period grouped by account (pie 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.error-message.UNKNOWN_ERROR=An unknown error occurred! financer.error-message.INVALID_ACCOUNT_TYPE=The selected account type is not valid! 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 26324bd..fee2ccd 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 @@ -159,11 +159,16 @@ financer.chart.account-group-expenses-current-period.title=Ausgaben in der aktue financer.chart.account-group-expenses-for-period.title=Ausgaben in der Periode vom {0} bis {1} gruppiert nach Konto-Gruppe 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 +financer.chart.expense-period-totals-current-year.x=Betrag +financer.chart.expense-period-totals-current-year.y=Periode 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_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.error-message.UNKNOWN_ERROR=Ein unbekannter Fehler ist aufgetreten! financer.error-message.INVALID_ACCOUNT_TYPE=Der ausgew\u00E4hlte Kontotyp ist ung\u00FCltig! diff --git a/financer-web-client/src/main/resources/static/changelog.txt b/financer-web-client/src/main/resources/static/changelog.txt index 91aa056..eeba98f 100644 --- a/financer-web-client/src/main/resources/static/changelog.txt +++ b/financer-web-client/src/main/resources/static/changelog.txt @@ -2,6 +2,7 @@ v17 -> v18: - Add readme to the footer - Translate error messages to German - Add chart type to chart description in chart type selection page +- Add expense period total chart v16 -> v17: - Add this changelog to the footer diff --git a/pom.xml b/pom.xml index 81e1c02..50ef3b6 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,11 @@ junit 4.12 + + com.google.guava + guava + 28.0-jre + de.77zzcx7.financer financer-common