Add calendar, SKIP and fix some bugs

This commit is contained in:
2020-10-09 16:44:05 +02:00
parent da3ce372b7
commit 2ee77f9d4f
29 changed files with 649 additions and 40 deletions

View File

@@ -0,0 +1,49 @@
package de.financer.calendar;
import de.financer.model.RecurringTransaction;
import java.time.LocalDate;
import java.time.temporal.ChronoField;
public class Day {
private LocalDate date;
private Iterable<RecurringTransaction> transactions;
public Day(LocalDate date) {
this.date = date;
}
public int getDay() {
return this.date.get(ChronoField.DAY_OF_MONTH);
}
public LocalDate getDate() {
return this.date;
}
public Iterable<RecurringTransaction> getTransactions() {
return transactions;
}
public boolean isPreviousOrNextMonth(Integer offset) {
LocalDate now = LocalDate.now().plusMonths(offset);
return !now.getMonth().equals(this.date.getMonth());
}
public boolean isToday() {
return LocalDate.now().equals(this.date);
}
public void setTransactions(Iterable<RecurringTransaction> transactions) {
this.transactions = transactions;
}
@Override
public String toString() {
return "Day{" +
"date=" + date +
", transactions=" + transactions +
'}';
}
}

View File

@@ -0,0 +1,32 @@
package de.financer.calendar;
import java.util.List;
public class Week {
private int weekNumber;
private List<Day> days;
public int getWeekNumber() {
return weekNumber;
}
public void setWeekNumber(int weekNumber) {
this.weekNumber = weekNumber;
}
public List<Day> getDays() {
return days;
}
public void setDays(List<Day> days) {
this.days = days;
}
@Override
public String toString() {
return "Week{" +
"weekNumber=" + weekNumber +
", days=" + days +
'}';
}
}

View File

@@ -6,6 +6,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import java.time.DayOfWeek;
import java.util.Currency;
@Configuration
@@ -19,6 +20,7 @@ public class FinancerConfig {
private String version;
private String currencyCode;
private Currency currency;
private DayOfWeek firstDayOfWeek;
public String getServerUrl() {
return serverUrl;
@@ -53,4 +55,12 @@ public class FinancerConfig {
public Currency getCurrency() {
return currency;
}
public DayOfWeek getFirstDayOfWeek() {
return firstDayOfWeek;
}
public void setFirstDayOfWeek(String firstDayOfWeek) {
this.firstDayOfWeek = DayOfWeek.valueOf(firstDayOfWeek);
}
}

View File

@@ -0,0 +1,146 @@
package de.financer.controller;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import de.financer.calendar.Day;
import de.financer.calendar.Week;
import de.financer.config.FinancerConfig;
import de.financer.dto.RecurringTransactionDueInRangeResponseDto;
import de.financer.dto.SearchTransactionsResponseDto;
import de.financer.template.FinancerRestTemplate;
import de.financer.template.exception.FinancerRestException;
import de.financer.util.ControllerUtils;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.util.UriComponentsBuilder;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.TextStyle;
import java.time.temporal.TemporalField;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
@Controller
public class CalendarController {
@Autowired
private FinancerConfig financerConfig;
@GetMapping("/recurringTransactionCalendar")
public String recurringTransactionCalendar(Model model, String offset) {
Integer offsetNumber = null;
if (!NumberUtils.isCreatable(offset)) {
// TODO
}
offsetNumber = Integer.valueOf(offset);
// 1. Add days for calendar headings
model.addAttribute("firstWeekDay", this.financerConfig.getFirstDayOfWeek());
model.addAttribute("secondWeekDay", this.financerConfig.getFirstDayOfWeek().plus(1));
model.addAttribute("thirdWeekDay", this.financerConfig.getFirstDayOfWeek().plus(2));
model.addAttribute("fourthWeekDay", this.financerConfig.getFirstDayOfWeek().plus(3));
model.addAttribute("fifthWeekDay", this.financerConfig.getFirstDayOfWeek().plus(4));
model.addAttribute("sixthWeekDay", this.financerConfig.getFirstDayOfWeek().plus(5));
model.addAttribute("seventhWeekDay", this.financerConfig.getFirstDayOfWeek().plus(6));
// 2. Calculate the days in the calendar
DayOfWeek firstDayOfWeek = this.financerConfig.getFirstDayOfWeek();
int tmpLastDayOfWeek = firstDayOfWeek.getValue() - 1;
DayOfWeek lastOfWeek = DayOfWeek.of(tmpLastDayOfWeek == 0 ? 7 : tmpLastDayOfWeek);
LocalDate today = LocalDate.now().plusMonths(offsetNumber);
LocalDate firstOfMonth = today.withDayOfMonth(1);
LocalDate start = null;
model.addAttribute("today", today);
if (firstOfMonth.getDayOfWeek() == firstDayOfWeek) {
// nothing to do, first is firstDayOfWeek
start = firstOfMonth;
}
// walk back till we have firstDayOfWeek
else {
LocalDate tmp = firstOfMonth;
while ((tmp = tmp.minusDays(1)).getDayOfWeek() != firstDayOfWeek) {
// Just spinning
}
start = tmp; // last firstDayOfWeek of last month
}
List<Day> days = new ArrayList<>();
LocalDate end = today.plusMonths(1).withDayOfMonth(1);
// Go till first lastOfWeek in next month, but only if the last day of the current month is
// not lastOfWeek
if (end.minusDays(1).getDayOfWeek() != lastOfWeek) {
while (end.getDayOfWeek() != lastOfWeek) {
end = end.plusDays(1);
}
end = end.plusDays(1); // because datesUntil parameter is exclusive
}
start.datesUntil(end).forEach((ld) -> days.add(new Day(ld)));
// 3. Get the recurring transactions due at the calculated days from the server
final String rangeStart = ControllerUtils.formatDate(this.financerConfig, days.get(0).getDate());
final String rangeEnd = ControllerUtils.formatDate(this.financerConfig, days.get(days.size() - 1).getDate());
try {
final UriComponentsBuilder transactionBuilder = UriComponentsBuilder
.fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.RT_GET_ALL_DUE_IN_RANGE));
transactionBuilder.queryParam("start", rangeStart);
transactionBuilder.queryParam("end", rangeEnd);
final Iterable<RecurringTransactionDueInRangeResponseDto> trxs = FinancerRestTemplate.exchangeGet(transactionBuilder,
new ParameterizedTypeReference<Iterable<RecurringTransactionDueInRangeResponseDto>>() {
});
// 3a. Match days and recurring transactions from server
IterableUtils.toList(trxs).stream().forEach(trx -> days.stream()
.filter(d -> d.getDate().equals(trx.getDate()))
.findFirst()
.get()
.setTransactions(trx.getRecurringTransactions()));
}
catch(FinancerRestException e) {
// TODO
model.addAttribute("errorMessage", e.getResponseReason().name());
}
// 4. Build the actual weeks from the data calculated before
List<Week> weeks = Lists.partition(days, 7).stream().map(l -> {
Week w = new Week();
LocalDate firstOfWeek = l.get(0).getDate();
TemporalField woy = WeekFields.of(Locale.getDefault()).weekOfWeekBasedYear(); // Not sure about using Default locale, but well ...
w.setWeekNumber(firstOfWeek.get(woy));
w.setDays(l);
return w;
}).collect(Collectors.toList());
model.addAttribute("weeks", weeks);
model.addAttribute("offset", offsetNumber);
ControllerUtils.addVersionAttribute(model, this.financerConfig);
ControllerUtils.addCurrencySymbol(model, this.financerConfig);
return "recurringTransaction/calendar";
}
}

View File

@@ -30,6 +30,7 @@ public enum Function {
RT_CREATE_RECURRING_TRANSACTION("recurringTransactions/createRecurringTransaction"),
RT_DELETE_RECURRING_TRANSACTION("recurringTransactions/deleteRecurringTransaction"),
RT_CREATE_TRANSACTION("recurringTransactions/createTransaction"),
RT_GET_ALL_DUE_IN_RANGE("recurringTransactions/getAllDueInRange"),
P_GET_CURRENT_EXPENSE_PERIOD("periods/getCurrentExpensePeriod"),
P_CLOSE_CURRENT_EXPENSE_PERIOD("periods/closeCurrentExpensePeriod"),

View File

@@ -1,6 +1,9 @@
package de.financer.controller;
import com.google.common.collect.Lists;
import de.financer.ResponseReason;
import de.financer.calendar.Day;
import de.financer.calendar.Week;
import de.financer.config.FinancerConfig;
import de.financer.model.*;
import de.financer.template.*;
@@ -15,8 +18,15 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.util.UriComponentsBuilder;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalField;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
@Controller
@@ -257,5 +267,4 @@ public class RecurringTransactionController {
return "redirect:/accountOverview";
}
}

View File

@@ -11,6 +11,7 @@ import de.financer.form.NewTransactionForm;
import de.financer.model.Account;
import de.financer.template.exception.FinancerRestException;
import de.financer.util.ControllerUtils;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
@@ -21,6 +22,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.util.UriComponentsBuilder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
@@ -56,10 +58,12 @@ public class TransactionController {
model.addAttribute("transactions", trxs);
model.addAttribute("transactionCount", IterableUtils.size(trxs));
}
catch(FinancerRestException e) {
// TODO
model.addAttribute("errorMessage", e.getResponseReason().name());
model.addAttribute("transactionCount", 0);
}
model.addAttribute("form", form);

View File

@@ -97,6 +97,6 @@ public class ControllerUtils {
public static void addCurrencySymbol(Model model, FinancerConfig financerConfig) {
// Add a space right in front of the currency symbol, so it's not glued to the amount
model.addAttribute("currencySymbol", " " + financerConfig.getCurrency().getSymbol());
model.addAttribute("currencySymbol", "&nbsp;" + financerConfig.getCurrency().getSymbol());
}
}

View File

@@ -35,4 +35,8 @@ spring.messages.basename=i18n/message
financer.currencyCode=EUR
push-service-client.serverUrl=http://localhost:8077/push-service-server/
logging.level.de.pushservice=DEBUG
logging.level.de.pushservice=DEBUG
# The first day of a week in a calendar
# Possible values: MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
financer.firstDayOfWeek=MONDAY

View File

@@ -10,6 +10,7 @@ financer.account-overview.available-actions.recurring-transaction-all=Show all r
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.available-actions.recurring-transaction-calendar=Recurring transaction calendar
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\:
@@ -118,6 +119,7 @@ financer.transaction-list.table.recurring.yes=Yes
financer.transaction-list.table.recurring.no=No
financer.transaction-list.table.taxRelevant.true=Yes
financer.transaction-list.table.taxRelevant.false=No
financer.transaction-list.transaction-count=Found {0} transactions
financer.recurring-to-transaction-with-amount.title=financer\: create transaction from recurring with amount
financer.recurring-to-transaction-with-amount.label.amount=Amount\:
@@ -174,6 +176,7 @@ financer.interval-type.YEARLY=Yearly
financer.holiday-weekend-type.SAME_DAY=Same day
financer.holiday-weekend-type.NEXT_WORKDAY=Next workday
financer.holiday-weekend-type.PREVIOUS_WORKDAY=Previous workday
financer.holiday-weekend-type.SKIP=Skip once
financer.account-type.BANK=Bank
financer.account-type.CASH=Cash
@@ -199,8 +202,10 @@ financer.heading.chart-select=financer\: select a chart to generate
financer.heading.chart-config-account-group-expenses-for-period=financer\: configure account group expenses for period chart
financer.heading.chart-config-account-expenses-for-period=financer\: configure account expenses for period chart
financer.heading.search-transactions=financer\: search transactions
financer.heading.recurring-transaction-calendar=financer\: recurring transaction calendar
financer.cancel-back-to-overview=Cancel and back to overview
financer.back-to-overview=Back to overview
financer.chart.account-group-expenses-current-period.title=Expenses of the current period grouped by account group
financer.chart.account-group-expenses-for-period.title=Expenses for period from {0} to {1} grouped by account group
@@ -218,6 +223,19 @@ 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.recurring-transaction-calendar.title=financer\: recurring transaction calendar
financer.calendar.calendarweek=CW
financer.calendar.weekdays.MONDAY=Monday
financer.calendar.weekdays.TUESDAY=Tuesday
financer.calendar.weekdays.WEDNESDAY=Wednesday
financer.calendar.weekdays.THURSDAY=Thursday
financer.calendar.weekdays.FRIDAY=Friday
financer.calendar.weekdays.SATURDAY=Saturday
financer.calendar.weekdays.SUNDAY=Sunday
financer.calendar.previous=Previous month
financer.calendar.next=Next month
financer.calendar.current=Current month
financer.error-message.UNKNOWN_ERROR=An unknown error occurred!
financer.error-message.INVALID_ACCOUNT_TYPE=The selected account type is not valid!
financer.error-message.FROM_ACCOUNT_NOT_FOUND=The specified from account has not been found!

View File

@@ -10,6 +10,7 @@ financer.account-overview.available-actions.recurring-transaction-all=Zeige alle
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.available-actions.recurring-transaction-calendar=Kalender wiederkehrende Buchung
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\:
@@ -118,6 +119,7 @@ financer.transaction-list.table.recurring.yes=Ja
financer.transaction-list.table.recurring.no=Nein
financer.transaction-list.table.taxRelevant.true=Ja
financer.transaction-list.table.taxRelevant.false=Nein
financer.transaction-list.transaction-count={0} Buchungen gefunden
financer.recurring-to-transaction-with-amount.title=financer\: Buchung mit Betrag aus wiederkehrender Buchung erstellen
financer.recurring-to-transaction-with-amount.label.amount=Betrag\:
@@ -174,6 +176,7 @@ financer.interval-type.YEARLY=J\u00E4hrlich
financer.holiday-weekend-type.SAME_DAY=Gleicher Tag
financer.holiday-weekend-type.NEXT_WORKDAY=N\u00E4chster Werktag
financer.holiday-weekend-type.PREVIOUS_WORKDAY=Vorheriger Werktag
financer.holiday-weekend-type.SKIP=Einmalig \u00FCberspringen
financer.account-type.BANK=Bank
financer.account-type.CASH=Bar
@@ -198,8 +201,10 @@ financer.heading.recurring-to-transaction-with-amount=financer\: Buchung mit Bet
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.search-transactions=financer\: Buchungen suchen
financer.heading.recurring-transaction-calendar=financer\: Kalender wiederkehrende Buchung
financer.cancel-back-to-overview=Abbrechen und zur \u00DCbersicht
financer.cancel-back-to-overview=Abbrechen und zur\u00FCck zur \u00DCbersicht
financer.back-to-overview=Zur\u00FCck zur \u00DCbersicht
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
@@ -217,6 +222,19 @@ financer.chart.name.ACCOUNT_EXPENSES_FOR_PERIOD=Ausgaben f\u00FCr eine konfiguri
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.recurring-transaction-calendar.title=financer\: Kalender wiederkehrende Buchung
financer.calendar.calendarweek=KW
financer.calendar.weekdays.MONDAY=Montag
financer.calendar.weekdays.TUESDAY=Dienstag
financer.calendar.weekdays.WEDNESDAY=Mittwoch
financer.calendar.weekdays.THURSDAY=Donnerstag
financer.calendar.weekdays.FRIDAY=Freitag
financer.calendar.weekdays.SATURDAY=Samstag
financer.calendar.weekdays.SUNDAY=Sonntag
financer.calendar.previous=Letzter Monat
financer.calendar.next=N\u00E4chster Monat
financer.calendar.current=Aktueller Monat
financer.error-message.UNKNOWN_ERROR=Ein unbekannter Fehler ist aufgetreten!
financer.error-message.INVALID_ACCOUNT_TYPE=Der ausgew\u00E4hlte Kontotyp ist ung\u00FCltig!
financer.error-message.FROM_ACCOUNT_NOT_FOUND=Das ausgew\u00E4hlte Von-Konto wurde nicht gefunden!

View File

@@ -1,3 +1,10 @@
v32 -> v33:
- Introduce Holiday/Weekend Type SKIP to skip an occurrence of a recurring transaction once, if the due date is either a
holiday or a weekend day
- Implement a calendar for recurring transactions
- Add result count to transaction search
- Fix a bug in the parsing of FQL
v31 -> v32:
- Integrate new push-service release with VAPID support for Chrome based browsers
@@ -26,13 +33,13 @@ v26 -> v27:
usage)
v25 -> v26:
- Close of the current expense period now creates null statistic entries for accounts that have not been used in
- Closing the current expense period now creates null statistic entries for accounts that have not been used in
bookings in the period to close. This way the average spending better reflects the period average
- Introduce period type GRAND TOTAL, that denotes a continuous, cumulative period, starting with the inception of the
financer app, without an end
- Introduce period type EXPENSE YEAR, that acts as a group for all expense periods in a year. This may not be a
calendar year, if e.g. the EXPENSE periods do not start at the first of every month. This includes all periods that
start the in the year. This is a preparation for extended reports and year-based booking
start in the year
- Transactions with a date in the past are now assigned to the proper EXPENSE and EXPENSE YEAR periods
- Add a fav icon
@@ -80,4 +87,4 @@ v16 -> v17:
- Add this changelog to the footer
- Locale of the recurring transaction reminder email is now configurable
- Recurring transaction indicator in transaction overview (account details) is now properly translated
- Add chart report to visualize the expenses of the current/a configurable period grouped by account
- Add chart report to visualize the expenses of the current/a configurable period grouped by account

View File

@@ -93,7 +93,8 @@ tr:hover {
text-align: center;
}
#status-container > span, div {
#status-container > span,
#status-container > div {
display: block;
}
@@ -135,7 +136,8 @@ input[type=submit] {
margin-top: 1em;
}
#footer-container > hr, div {
#footer-container > hr,
#footer-container > div {
display: block;
}
@@ -203,4 +205,112 @@ input[type=submit] {
#search-transactions-fql-detail > * {
font-size: 0.7em;
}
#cal {
width: 100%;
margin-top: 1.5em;
margin-bottom: 1.5em;
border-collapse: collapse;
}
#cal th {
border-bottom: 1px solid #ddd;
border-right: 1px solid #ddd;
}
#cal th:nth-last-child(1) {
border-right: 0px solid #ddd;
}
#cal-control-con {
width: 100%;
}
#cal-control-con-table {
width: 100%;
}
#cal-control-con-table > tbody > tr > * {
width: 33%;
text-align: center;
}
#cal-control-con-table > tbody > tr:hover {
background-color: transparent;
}
.cal-day-previous {
background-color: #F0EEEE;
}
.cal-day-con {
display: inline-block;
width: 100%;
}
.cal-week-row:hover {
background-color: transparent;
}
#cal-header-row:hover {
background-color: transparent;
}
.cal-calendarweek {
text-align: center;
vertical-align: top;
}
.cal-calendarweek-con-content {
margin-top: 0.5em;
}
.cal-day-con-content-con-text {
margin-left: 0.5em;
margin-right: 0.5em;
margin-top: 0.3em;
margin-bottom: 0.3em;
background-color: #e9f9f9;
border: 1px;
border-style: solid;
border-color: #b9dFdF;
border-radius: 4px;
padding-left: 1em;
padding-right: 1em;
color: #000000;
display: inline-block;
}
#transaction-list-container-count-con-text {
margin-bottom: 0px;
margin-top: 2em;
}
#transaction-table {
margin-top: 0.2em;
}
.cal-day-con-number-con-text {
margin: 0.5em;
color: #444242;
}
.cal-day {
vertical-align: top;
border-bottom: 1px solid #ddd;
border-right: 1px solid #ddd;
}
.cal-calendarweek {
border-right: 1px solid #ddd;
border-bottom: 1px solid #ddd;
}
.cal-week-row td:nth-last-child(1) {
border-right: 0px solid #ddd;
}
.cal-day-con-today {
background-color: #f5e5b8;
}

View File

@@ -199,6 +199,4 @@
====================
This chapter lists planned features. The list is in no particular order:
- Transaction import from online banking (file based)
- Extended reports, e.g. forecasting based on recurring transactions and average spending
- Receivable account type
- Edit masks for accounts, transactions, recurring transactions
- Extended reports, e.g. forecasting based on recurring transactions and average spending

View File

@@ -17,7 +17,7 @@
</div>
<div id="balance-container">
<span th:text="#{financer.account-details.details.balance}"/>
<span th:text="${#numbers.formatDecimal(account.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
<span th:utext="${#numbers.formatDecimal(account.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
</div>
<div id="group-container" th:if="${account.accountGroup != null}">
<span th:text="#{financer.account-details.details.group}"/>

View File

@@ -17,12 +17,12 @@
<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}"/>
<span th:utext="${#numbers.formatDecimal(currentAssets/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
</div>
<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}" />
<span th:utext="${#numbers.formatDecimal(currentExpenses/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}" />
</a>
</div>
<div>
@@ -58,6 +58,8 @@
th:text="#{financer.account-overview.available-actions.create-recurring-transaction}"/>
<a th:href="@{/recurringTransactionAll}"
th:text="#{financer.account-overview.available-actions.recurring-transaction-all}"/>
<a th:href="@{'/recurringTransactionCalendar?offset=0'}"
th:text="#{financer.account-overview.available-actions.recurring-transaction-calendar}"/>
</div>
<div id="action-container-sub-period">
<a th:href="@{/closePeriod}"
@@ -91,14 +93,14 @@
<td>
<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:utext="${#numbers.formatDecimal(acc.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
<td class="hideable-column"
th:if="${acc.spendingCurrentExpensePeriod != null}"
th:text="${#numbers.formatDecimal(acc.spendingCurrentExpensePeriod/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"
th:utext="${#numbers.formatDecimal(acc.spendingCurrentExpensePeriod/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"
th:classappend="${acc.spendingCurrentExpensePeriod > acc.averageSpendingExpensePeriod} ? overspend"/>
<td class="hideable-column"
th:if="${acc.averageSpendingExpensePeriod != null}"
th:text="${#numbers.formatDecimal(acc.averageSpendingExpensePeriod/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
th:utext="${#numbers.formatDecimal(acc.averageSpendingExpensePeriod/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
<td class="hideable-column" th:if="${acc.spendingCurrentExpensePeriod == null}">-</td>
<td class="hideable-column" th:if="${acc.averageSpendingExpensePeriod == null}">-</td>
<td class="hideable-column" th:text="${acc.accountGroup?.name}"/>

View File

@@ -0,0 +1,63 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="#{financer.recurring-transaction-calendar.title}"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" th:href="@{/css/main.css}">
<link rel="shortcut icon" th:href="@{/favicon.ico}"/>
</head>
<body>
<h1 th:text="#{financer.heading.recurring-transaction-calendar} + ' — ' + ${#temporals.monthName(today)} + ' ' + ${#temporals.year(today)}"/>
<span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/>
<a th:href="@{/accountOverview}" th:text="#{financer.back-to-overview}"/>
<div id="calendar-container" th:fragment="recurring-transaction-calendar">
<table id="cal">
<tr id="cal-header-row">
<th class="header-col" th:text="#{'financer.calendar.calendarweek'}"/>
<th class="header-col" th:text="#{'financer.calendar.weekdays.' + ${firstWeekDay}}"/>
<th class="header-col" th:text="#{'financer.calendar.weekdays.' + ${secondWeekDay}}"/>
<th class="header-col" th:text="#{'financer.calendar.weekdays.' + ${thirdWeekDay}}"/>
<th class="header-col" th:text="#{'financer.calendar.weekdays.' + ${fourthWeekDay}}"/>
<th class="header-col" th:text="#{'financer.calendar.weekdays.' + ${fifthWeekDay}}"/>
<th class="header-col" th:text="#{'financer.calendar.weekdays.' + ${sixthWeekDay}}"/>
<th class="header-col" th:text="#{'financer.calendar.weekdays.' + ${seventhWeekDay}}"/>
</tr>
<tr class="cal-week-row" th:each="week : ${weeks}">
<td class="cal-calendarweek">
<div class="cal-calendarweek-con">
<p class="cal-calendarweek-con-content" th:text="${week.weekNumber}"/>
</div>
</td>
<td th:class="${day.isPreviousOrNextMonth(offset) ? 'cal-day-previous cal-day' : 'cal-day'}" th:each="day : ${week.days}">
<div th:class="${day.today ? 'cal-day-con cal-day-con-today' : 'cal-day-con'}">
<div class="cal-day-con-number-con">
<p class="cal-day-con-number-con-text" th:text="${day.day}"/>
</div>
<div th:if="${!day.isPreviousOrNextMonth(offset)}" class="cal-day-con-content">
<div class="cal-day-con-content-con" th:each="transaction : ${day.transactions}">
<p class="cal-day-con-content-con-text" th:utext="${transaction.description + ' — ' + #numbers.formatDecimal(transaction.amount/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}" />
</div>
</div>
</div>
</td>
</table>
<div id="cal-control-con">
<table id="cal-control-con-table">
<tr>
<td>
<a id="cal-control-con-prev" th:href="@{'/recurringTransactionCalendar?offset=' + ${offset-1} + ''}" th:text="#{financer.calendar.previous}" />
</td>
<td>
<a id="cal-control-con-curr" th:href="@{'/recurringTransactionCalendar?offset=0'}" th:text="#{financer.calendar.current}" />
</td>
<td>
<a id="cal-control-con-next" th:href="@{'/recurringTransactionCalendar?offset=' + ${offset+1} + ''}" th:text="#{financer.calendar.next}" />
</td>
</tr>
</table>
</div>
</div>
<div th:replace="includes/footer :: footer"/>
</body>
</html>

View File

@@ -16,12 +16,12 @@
<label for="selectFromAccount" th:text="#{financer.recurring-transaction-new.label.from-account}"/>
<select id="selectFromAccount" th:field="*{fromAccountKey}">
<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})}"/>
th:utext="#{'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 : ${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})}"/>
th:utext="#{'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}"/>
<input type="text" id="inputAmount" th:field="*{amount}"/>

View File

@@ -35,7 +35,7 @@
</td>
<td th:text="${#temporals.format(rt.firstOccurrence)}"/>
<td th:text="${#temporals.format(rt.lastOccurrence)}"/>
<td th:text="${#numbers.formatDecimal(rt.amount/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
<td th:utext="${#numbers.formatDecimal(rt.amount/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
<td th:text="${rt.description}"/>
<td th:text="#{'financer.interval-type.' + ${rt.intervalType}}"/>
<td th:text="#{'financer.holiday-weekend-type.' + ${rt.holidayWeekendType}}"/>

View File

@@ -16,12 +16,12 @@
<label for="selectFromAccount" th:text="#{financer.transaction-new.label.from-account}"/>
<select id="selectFromAccount" th:field="*{fromAccountKey}">
<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})}"/>
th:utext="#{'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 : ${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})}"/>
th:utext="#{'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}"/>
<input type="text" id="inputAmount" th:field="*{amount}"/>

View File

@@ -1,4 +1,7 @@
<div id="transaction-list-container" th:fragment="transaction-list">
<div id="transaction-list-container-count-con" th:if="${transactionCount != null}">
<p id="transaction-list-container-count-con-text" th:text="#{'financer.transaction-list.transaction-count'(${transactionCount})}"/>
</div>
<table id="transaction-table">
<tr>
<th class="hideable-column" th:text="#{financer.transaction-list.table-header.id}"/>
@@ -16,7 +19,7 @@
<td th:text="${transaction.fromAccount.key}" />
<td th:text="${transaction.toAccount.key}" />
<td th:text="${#temporals.format(transaction.date)}" />
<td th:text="${#numbers.formatDecimal(transaction.amount/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
<td th:utext="${#numbers.formatDecimal(transaction.amount/100D, 1, 'DEFAULT', 2, 'DEFAULT') + currencySymbol}"/>
<td th:text="${transaction.description}" />
<td th:if="${transaction.recurring}" th:text="#{financer.transaction-list.table.recurring.yes}" />
<td th:if="${!transaction.recurring}" th:text="#{financer.transaction-list.table.recurring.no}" />