From 67bd7f951ae4bb26b979ca7ebe3071e857686307 Mon Sep 17 00:00:00 2001 From: MK13 Date: Sun, 24 Mar 2019 23:28:15 +0100 Subject: [PATCH 01/32] WIP Initial commit for financer web client --- .gitignore | 2 + pom.xml | 141 +++++++++++++++++ .../java/de/financer/FinancerApplication.java | 20 +++ src/main/java/de/financer/ResponseReason.java | 53 +++++++ .../de/financer/config/FinancerConfig.java | 31 ++++ .../controller/AccountController.java | 137 +++++++++++++++++ .../java/de/financer/controller/Function.java | 32 ++++ .../RecurringTransactionController.java | 113 ++++++++++++++ .../controller/TransactionController.java | 59 ++++++++ .../NoExceptionResponseErrorHandler.java | 18 +++ .../template/FinancerRestTemplate.java | 42 ++++++ .../template/GetAccountByKeyTemplate.java | 21 +++ .../template/GetAllAccountsTemplate.java | 16 ++ ...llActiveRecurringTransactionsTemplate.java | 16 ++ ...RecurringTransactionsDueTodayTemplate.java | 16 ++ .../GetAllRecurringTransactionsTemplate.java | 16 ++ .../GetAllTransactionsForAccountTemplate.java | 21 +++ .../controller/template/StringTemplate.java | 13 ++ .../java/de/financer/form/NewAccountForm.java | 22 +++ .../form/NewRecurringTransactionForm.java | 76 ++++++++++ .../de/financer/form/NewTransactionForm.java | 49 ++++++ src/main/java/de/financer/model/Account.java | 45 ++++++ .../java/de/financer/model/AccountStatus.java | 20 +++ .../java/de/financer/model/AccountType.java | 38 +++++ .../de/financer/model/HolidayWeekendType.java | 71 +++++++++ .../java/de/financer/model/IntervalType.java | 36 +++++ .../financer/model/RecurringTransaction.java | 83 ++++++++++ .../java/de/financer/model/Transaction.java | 65 ++++++++ .../de/financer/util/ControllerUtils.java | 50 ++++++ .../comparator/AccountByTypeComparator.java | 23 +++ .../config/application-dev.properties | 2 + .../resources/config/application.properties | 25 +++ src/main/resources/i18n/message.properties | 142 ++++++++++++++++++ .../resources/i18n/message_de_DE.properties | 116 ++++++++++++++ src/main/resources/static/css/main.css | 67 +++++++++ .../templates/account/accountDetails.html | 59 ++++++++ .../templates/account/accountOverview.html | 54 +++++++ .../templates/account/newAccount.html | 22 +++ .../newRecurringTransaction.html | 45 ++++++ .../recurringTransactionList.html | 53 +++++++ .../templates/transaction/newTransaction.html | 33 ++++ .../financer/FinancerApplicationBootTest.java | 31 ++++ .../application-integrationtest.properties | 5 + 43 files changed, 1999 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/de/financer/FinancerApplication.java create mode 100644 src/main/java/de/financer/ResponseReason.java create mode 100644 src/main/java/de/financer/config/FinancerConfig.java create mode 100644 src/main/java/de/financer/controller/AccountController.java create mode 100644 src/main/java/de/financer/controller/Function.java create mode 100644 src/main/java/de/financer/controller/RecurringTransactionController.java create mode 100644 src/main/java/de/financer/controller/TransactionController.java create mode 100644 src/main/java/de/financer/controller/handler/NoExceptionResponseErrorHandler.java create mode 100644 src/main/java/de/financer/controller/template/FinancerRestTemplate.java create mode 100644 src/main/java/de/financer/controller/template/GetAccountByKeyTemplate.java create mode 100644 src/main/java/de/financer/controller/template/GetAllAccountsTemplate.java create mode 100644 src/main/java/de/financer/controller/template/GetAllActiveRecurringTransactionsTemplate.java create mode 100644 src/main/java/de/financer/controller/template/GetAllRecurringTransactionsDueTodayTemplate.java create mode 100644 src/main/java/de/financer/controller/template/GetAllRecurringTransactionsTemplate.java create mode 100644 src/main/java/de/financer/controller/template/GetAllTransactionsForAccountTemplate.java create mode 100644 src/main/java/de/financer/controller/template/StringTemplate.java create mode 100644 src/main/java/de/financer/form/NewAccountForm.java create mode 100644 src/main/java/de/financer/form/NewRecurringTransactionForm.java create mode 100644 src/main/java/de/financer/form/NewTransactionForm.java create mode 100644 src/main/java/de/financer/model/Account.java create mode 100644 src/main/java/de/financer/model/AccountStatus.java create mode 100644 src/main/java/de/financer/model/AccountType.java create mode 100644 src/main/java/de/financer/model/HolidayWeekendType.java create mode 100644 src/main/java/de/financer/model/IntervalType.java create mode 100644 src/main/java/de/financer/model/RecurringTransaction.java create mode 100644 src/main/java/de/financer/model/Transaction.java create mode 100644 src/main/java/de/financer/util/ControllerUtils.java create mode 100644 src/main/java/de/financer/util/comparator/AccountByTypeComparator.java create mode 100644 src/main/resources/config/application-dev.properties create mode 100644 src/main/resources/config/application.properties create mode 100644 src/main/resources/i18n/message.properties create mode 100644 src/main/resources/i18n/message_de_DE.properties create mode 100644 src/main/resources/static/css/main.css create mode 100644 src/main/resources/templates/account/accountDetails.html create mode 100644 src/main/resources/templates/account/accountOverview.html create mode 100644 src/main/resources/templates/account/newAccount.html create mode 100644 src/main/resources/templates/recurringTransaction/newRecurringTransaction.html create mode 100644 src/main/resources/templates/recurringTransaction/recurringTransactionList.html create mode 100644 src/main/resources/templates/transaction/newTransaction.html create mode 100644 src/test/java/de/financer/FinancerApplicationBootTest.java create mode 100644 src/test/resources/application-integrationtest.properties diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b3cec76 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +financer-web-client.log* +.attach* \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..aab892e --- /dev/null +++ b/pom.xml @@ -0,0 +1,141 @@ + + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.1.2.RELEASE + + + + de.77zzcx7.financer + financer-web-client + 1-SNAPSHOT + ${packaging.type} + The web client part of the financer application - a simple app to manage your personal finances + financer-web-client + + + UTF-8 + 1.9 + 1.9 + 1.9 + jar + dev + + 000001 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.apache.commons + commons-lang3 + + + org.apache.commons + commons-collections4 + 4.3 + + + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.restdocs + spring-restdocs-mockmvc + test + + + junit + junit + 4.12 + test + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest + + + + + + + + + integration-tests + + + + maven-surefire-plugin + + + integration-test + + test + + + + none + + + **/*IntegrationTest + + + + + + + + + + + build-war + + war + postgres + + + ${project.artifactId}##${parallelDeploymentVersion} + + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + + + diff --git a/src/main/java/de/financer/FinancerApplication.java b/src/main/java/de/financer/FinancerApplication.java new file mode 100644 index 0000000..8737a0b --- /dev/null +++ b/src/main/java/de/financer/FinancerApplication.java @@ -0,0 +1,20 @@ +package de.financer; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@SpringBootApplication +public class FinancerApplication extends SpringBootServletInitializer { + public static void main(String[] args) { + SpringApplication.run(FinancerApplication.class); + } + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(FinancerApplication.class); + } +} diff --git a/src/main/java/de/financer/ResponseReason.java b/src/main/java/de/financer/ResponseReason.java new file mode 100644 index 0000000..88532a1 --- /dev/null +++ b/src/main/java/de/financer/ResponseReason.java @@ -0,0 +1,53 @@ +package de.financer; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +public enum ResponseReason { + OK(HttpStatus.OK), + UNKNOWN_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + INVALID_ACCOUNT_TYPE(HttpStatus.INTERNAL_SERVER_ERROR), + INVALID_ACCOUNT_KEY(HttpStatus.INTERNAL_SERVER_ERROR), + FROM_ACCOUNT_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR), + TO_ACCOUNT_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR), + FROM_AND_TO_ACCOUNT_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR), + INVALID_DATE_FORMAT(HttpStatus.INTERNAL_SERVER_ERROR), + MISSING_DATE(HttpStatus.INTERNAL_SERVER_ERROR), + AMOUNT_ZERO(HttpStatus.INTERNAL_SERVER_ERROR), + MISSING_AMOUNT(HttpStatus.INTERNAL_SERVER_ERROR), + INVALID_BOOKING_ACCOUNTS(HttpStatus.INTERNAL_SERVER_ERROR), + MISSING_HOLIDAY_WEEKEND_TYPE(HttpStatus.INTERNAL_SERVER_ERROR), + INVALID_HOLIDAY_WEEKEND_TYPE(HttpStatus.INTERNAL_SERVER_ERROR), + MISSING_INTERVAL_TYPE(HttpStatus.INTERNAL_SERVER_ERROR), + INVALID_INTERVAL_TYPE(HttpStatus.INTERNAL_SERVER_ERROR), + MISSING_FIRST_OCCURRENCE(HttpStatus.INTERNAL_SERVER_ERROR), + INVALID_FIRST_OCCURRENCE_FORMAT(HttpStatus.INTERNAL_SERVER_ERROR), + INVALID_LAST_OCCURRENCE_FORMAT(HttpStatus.INTERNAL_SERVER_ERROR), + MISSING_RECURRING_TRANSACTION_ID(HttpStatus.INTERNAL_SERVER_ERROR), + INVALID_RECURRING_TRANSACTION_ID(HttpStatus.INTERNAL_SERVER_ERROR), + RECURRING_TRANSACTION_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR), + MISSING_TRANSACTION_ID(HttpStatus.INTERNAL_SERVER_ERROR), + INVALID_TRANSACTION_ID(HttpStatus.INTERNAL_SERVER_ERROR), + TRANSACTION_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR), + ACCOUNT_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR); + + private HttpStatus httpStatus; + + ResponseReason(HttpStatus httpStatus) { + this.httpStatus = httpStatus; + } + + public ResponseEntity toResponseEntity() { + return new ResponseEntity<>(this.name(), this.httpStatus); + } + + public static ResponseReason fromResponseEntity(ResponseEntity entity) { + for (ResponseReason reason : values()) { + if (reason.name().equals(entity.getBody())) { + return reason; + } + } + + return UNKNOWN_ERROR; + } +} diff --git a/src/main/java/de/financer/config/FinancerConfig.java b/src/main/java/de/financer/config/FinancerConfig.java new file mode 100644 index 0000000..91a368d --- /dev/null +++ b/src/main/java/de/financer/config/FinancerConfig.java @@ -0,0 +1,31 @@ +package de.financer.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties(prefix = "financer") +public class FinancerConfig { + private static final Logger LOGGER = LoggerFactory.getLogger(FinancerConfig.class); + + private String serverUrl; + private String dateFormat; + + public String getServerUrl() { + return serverUrl; + } + + public void setServerUrl(String serverUrl) { + this.serverUrl = serverUrl; + } + + public String getDateFormat() { + return dateFormat; + } + + public void setDateFormat(String dateFormat) { + this.dateFormat = dateFormat; + } +} diff --git a/src/main/java/de/financer/controller/AccountController.java b/src/main/java/de/financer/controller/AccountController.java new file mode 100644 index 0000000..9fa2b36 --- /dev/null +++ b/src/main/java/de/financer/controller/AccountController.java @@ -0,0 +1,137 @@ +package de.financer.controller; + +import de.financer.ResponseReason; +import de.financer.config.FinancerConfig; +import de.financer.controller.template.*; +import de.financer.form.NewAccountForm; +import de.financer.model.*; +import de.financer.util.ControllerUtils; +import org.apache.commons.collections4.IterableUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.util.UriComponentsBuilder; + +@Controller +public class AccountController { + @Autowired + private FinancerConfig financerConfig; + + @GetMapping("/accountOverview") + public String accountOverview(String showClosed, Model model) { + final ResponseEntity> response = new GetAllAccountsTemplate().exchange(this.financerConfig); + final ResponseEntity> rtDtRes = new GetAllRecurringTransactionsDueTodayTemplate() + .exchange(this.financerConfig); + final ResponseEntity> rtAllActRes = new GetAllActiveRecurringTransactionsTemplate() + .exchange(this.financerConfig); + final boolean showClosedBoolean = BooleanUtils.toBoolean(showClosed); + + model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(response.getBody(), showClosedBoolean)); + model.addAttribute("rtDueTodayCount", IterableUtils.size(rtDtRes.getBody())); + model.addAttribute("rtAllActiveCount", IterableUtils.size(rtAllActRes.getBody())); + model.addAttribute("showClosed", showClosedBoolean); + + return "account/accountOverview"; + } + + @GetMapping("/newAccount") + public String newAccount(Model model) { + model.addAttribute("accounttypes", AccountType.valueList()); + model.addAttribute("newAccountForm", new NewAccountForm()); + + return "account/newAccount"; + } + + @PostMapping("/saveAccount") + public String saveAccont(NewAccountForm newAccountForm, Model model) { + final UriComponentsBuilder builder = UriComponentsBuilder + .fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.ACC_CREATE_ACCOUNT)) + .queryParam("key", newAccountForm.getKey()) + .queryParam("type", newAccountForm.getType()); + + final ResponseEntity response = new StringTemplate().exchange(builder); + final ResponseReason responseReason = ResponseReason.fromResponseEntity(response); + + if (!ResponseReason.OK.equals(responseReason)) { + model.addAttribute("form", newAccountForm); + model.addAttribute("accounttypes", AccountType.valueList()); + model.addAttribute("errorMessage", responseReason.name()); + + return "account/newAccount"; + } + + return "redirect:/accountOverview"; + } + + @GetMapping("/accountDetails") + public String accountDetails(String key, Model model) { + final ResponseEntity response = new GetAccountByKeyTemplate().exchange(this.financerConfig, key); + final Account account = response.getBody(); + final ResponseEntity> transactionResponse = new GetAllTransactionsForAccountTemplate() + .exchange(this.financerConfig, account.getKey()); + + model.addAttribute("account", account); + model.addAttribute("transactions", IterableUtils.toList(transactionResponse.getBody())); + model.addAttribute("isClosed", AccountStatus.CLOSED.equals(account.getStatus())); + + return "account/accountDetails"; + } + + @GetMapping("/closeAccount") + public String closeAccount(String key, Model model) { + final UriComponentsBuilder closeBuilder = UriComponentsBuilder + .fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.ACC_CLOSE_ACCOUNT)) + .queryParam("key", key); + + final ResponseEntity closeResponse = new StringTemplate().exchange(closeBuilder); + + final ResponseReason responseReason = ResponseReason.fromResponseEntity(closeResponse); + + if (!ResponseReason.OK.equals(responseReason)) { + final ResponseEntity response = new GetAccountByKeyTemplate().exchange(this.financerConfig, key); + final Account account = response.getBody(); + final ResponseEntity> transactionResponse = new GetAllTransactionsForAccountTemplate() + .exchange(this.financerConfig, account.getKey()); + + model.addAttribute("account", account); + model.addAttribute("transactions", IterableUtils.toList(transactionResponse.getBody())); + model.addAttribute("isClosed", AccountStatus.CLOSED.equals(account.getStatus())); + model.addAttribute("errorMessage", responseReason.name()); + + return "account/accountDetails"; + } + + return "redirect:/accountOverview"; + } + + @GetMapping("/openAccount") + public String openAccount(String key, Model model) { + final UriComponentsBuilder openBuilder = UriComponentsBuilder + .fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.ACC_OPEN_ACCOUNT)) + .queryParam("key", key); + + final ResponseEntity closeResponse = new StringTemplate().exchange(openBuilder); + + final ResponseReason responseReason = ResponseReason.fromResponseEntity(closeResponse); + + if (!ResponseReason.OK.equals(responseReason)) { + final ResponseEntity response = new GetAccountByKeyTemplate().exchange(this.financerConfig, key); + final Account account = response.getBody(); + final ResponseEntity> transactionResponse = new GetAllTransactionsForAccountTemplate() + .exchange(this.financerConfig, account.getKey()); + + model.addAttribute("account", account); + model.addAttribute("transactions", IterableUtils.toList(transactionResponse.getBody())); + model.addAttribute("isClosed", AccountStatus.CLOSED.equals(account.getStatus())); + model.addAttribute("errorMessage", responseReason.name()); + + return "account/accountDetails"; + } + + return "redirect:/accountOverview"; + } +} diff --git a/src/main/java/de/financer/controller/Function.java b/src/main/java/de/financer/controller/Function.java new file mode 100644 index 0000000..e97bdcb --- /dev/null +++ b/src/main/java/de/financer/controller/Function.java @@ -0,0 +1,32 @@ +package de.financer.controller; + +public enum Function { + ACC_GET_BY_KEY("accounts/getByKey"), + ACC_GET_ALL("accounts/getAll"), + ACC_CREATE_ACCOUNT("accounts/createAccount"), + ACC_CLOSE_ACCOUNT("accounts/closeAccount"), + ACC_OPEN_ACCOUNT("accounts/openAccount"), + + TR_GET_ALL("transactions/getAll"), + TR_GET_ALL_FOR_ACCOUNT("transactions/getAllForAccount"), + TR_CREATE_TRANSACTION("transactions/createTransaction"), + TR_DELETE_TRANSACTION("transactions/deleteTransaction"), + + RT_GET_ALL("recurringTransactions/getAll"), + RT_GET_ALL_ACTIVE("recurringTransactions/getAllActive"), + RT_GET_ALL_FOR_ACCOUNT("recurringTransactions/getAllForAccount"), + RT_GET_ALL_DUE_TODAY("recurringTransactions/getAllDueToday"), + RT_CREATE_RECURRING_TRANSACTION("recurringTransactions/createRecurringTransaction"), + RT_DELETE_RECURRING_TRANSACTION("recurringTransactions/deleteRecurringTransaction"), + RT_CREATE_TRANSACTION("recurringTransactions/createTransaction"); + + private String path; + + Function(String path) { + this.path = path; + } + + public String getPath() { + return this.path; + } +} diff --git a/src/main/java/de/financer/controller/RecurringTransactionController.java b/src/main/java/de/financer/controller/RecurringTransactionController.java new file mode 100644 index 0000000..397efc1 --- /dev/null +++ b/src/main/java/de/financer/controller/RecurringTransactionController.java @@ -0,0 +1,113 @@ +package de.financer.controller; + +import de.financer.ResponseReason; +import de.financer.config.FinancerConfig; +import de.financer.controller.template.*; +import de.financer.form.NewRecurringTransactionForm; +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; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.util.UriComponentsBuilder; + +@Controller +public class RecurringTransactionController { + + @Autowired + private FinancerConfig financerConfig; + + @GetMapping("/newRecurringTransaction") + public String newRecurringTransaction(Model model) { + final ResponseEntity> response = new GetAllAccountsTemplate().exchange(this.financerConfig); + + model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(response.getBody())); + model.addAttribute("intervalTypes", IntervalType.valueList()); + model.addAttribute("holidayWeekendTypes", HolidayWeekendType.valueList()); + model.addAttribute("newRecurringTransactionForm", new NewRecurringTransactionForm()); + + return "recurringTransaction/newRecurringTransaction"; + } + + @PostMapping("/saveRecurringTransaction") + public String saveRecurringTransaction(NewRecurringTransactionForm newRecurringTransactionForm, Model model) { + final UriComponentsBuilder builder = UriComponentsBuilder + .fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.RT_CREATE_RECURRING_TRANSACTION)) + .queryParam("fromAccountKey", newRecurringTransactionForm + .getFromAccountKey()) + .queryParam("toAccountKey", newRecurringTransactionForm + .getToAccountKey()) + .queryParam("amount", newRecurringTransactionForm + .getAmount()) + .queryParam("firstOccurrence", ControllerUtils + .formatDate(this.financerConfig, newRecurringTransactionForm + .getFirstOccurrence())) + .queryParam("lastOccurrence", ControllerUtils + .formatDate(this.financerConfig, newRecurringTransactionForm + .getLastOccurrence())) + .queryParam("holidayWeekendType", newRecurringTransactionForm + .getHolidayWeekendType()) + .queryParam("intervalType", newRecurringTransactionForm + .getIntervalType()) + .queryParam("description", newRecurringTransactionForm + .getDescription()); + + final ResponseEntity response = new StringTemplate().exchange(builder); + + final ResponseReason responseReason = ResponseReason.fromResponseEntity(response); + + if (!ResponseReason.OK.equals(responseReason)) { + final ResponseEntity> getAllResponse = new GetAllAccountsTemplate() + .exchange(this.financerConfig); + + model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(getAllResponse.getBody())); + model.addAttribute("intervalTypes", IntervalType.valueList()); + model.addAttribute("holidayWeekendTypes", HolidayWeekendType.valueList()); + model.addAttribute("form", newRecurringTransactionForm); + model.addAttribute("errorMessage", responseReason.name()); + + return "recurringTransaction/newRecurringTransaction"; + } + + return "redirect:/accountOverview"; + } + + @GetMapping("/recurringTransactionDueToday") + public String recurringTransactionDueToday(Model model) { + final ResponseEntity> response = new GetAllRecurringTransactionsDueTodayTemplate() + .exchange(this.financerConfig); + + model.addAttribute("recurringTransactions", response.getBody()); + model.addAttribute("subTitle", "dueToday"); + + return "recurringTransaction/recurringTransactionList"; + } + + @GetMapping("/recurringTransactionActive") + public String recurringTransactionActive(Model model) { + final ResponseEntity> response = new GetAllActiveRecurringTransactionsTemplate() + .exchange(this.financerConfig); + + model.addAttribute("recurringTransactions", response.getBody()); + model.addAttribute("subTitle", "active"); + + return "recurringTransaction/recurringTransactionList"; + } + + @GetMapping("/recurringTransactionAll") + public String recurringTransactionAll(Model model) { + final ResponseEntity> response = new GetAllRecurringTransactionsTemplate() + .exchange(this.financerConfig); + + model.addAttribute("recurringTransactions", response.getBody()); + model.addAttribute("subTitle", "all"); + + return "recurringTransaction/recurringTransactionList"; + } +} diff --git a/src/main/java/de/financer/controller/TransactionController.java b/src/main/java/de/financer/controller/TransactionController.java new file mode 100644 index 0000000..6eeec4c --- /dev/null +++ b/src/main/java/de/financer/controller/TransactionController.java @@ -0,0 +1,59 @@ +package de.financer.controller; + +import de.financer.ResponseReason; +import de.financer.config.FinancerConfig; +import de.financer.controller.template.GetAllAccountsTemplate; +import de.financer.controller.template.StringTemplate; +import de.financer.form.NewTransactionForm; +import de.financer.model.Account; +import de.financer.util.ControllerUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.util.UriComponentsBuilder; + +@Controller +public class TransactionController { + + @Autowired + private FinancerConfig financerConfig; + + @GetMapping("/newTransaction") + public String newTransaction(Model model) { + final ResponseEntity> response = new GetAllAccountsTemplate().exchange(this.financerConfig); + + model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(response.getBody())); + model.addAttribute("newTransactionForm", new NewTransactionForm()); + + return "transaction/newTransaction"; + } + + @PostMapping("/saveTransaction") + public String saveTransaction(NewTransactionForm newTransactionForm, Model model) { + final UriComponentsBuilder builder = UriComponentsBuilder + .fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.TR_CREATE_TRANSACTION)) + .queryParam("fromAccountKey", newTransactionForm.getFromAccountKey()) + .queryParam("toAccountKey", newTransactionForm.getToAccountKey()) + .queryParam("amount", newTransactionForm.getAmount()) + .queryParam("date", ControllerUtils.formatDate(this.financerConfig, newTransactionForm.getDate())) + .queryParam("description", newTransactionForm.getDescription()); + + final ResponseEntity response = new StringTemplate().exchange(builder); + final ResponseReason responseReason = ResponseReason.fromResponseEntity(response); + + if (!ResponseReason.OK.equals(responseReason)) { + final ResponseEntity> accRes = new GetAllAccountsTemplate().exchange(this.financerConfig); + + model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(accRes.getBody())); + model.addAttribute("form", newTransactionForm); + model.addAttribute("errorMessage", responseReason.name()); + + return "transaction/newTransaction"; + } + + return "redirect:/accountOverview"; + } +} diff --git a/src/main/java/de/financer/controller/handler/NoExceptionResponseErrorHandler.java b/src/main/java/de/financer/controller/handler/NoExceptionResponseErrorHandler.java new file mode 100644 index 0000000..16c6eaf --- /dev/null +++ b/src/main/java/de/financer/controller/handler/NoExceptionResponseErrorHandler.java @@ -0,0 +1,18 @@ +package de.financer.controller.handler; + +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.ResponseErrorHandler; + +import java.io.IOException; + +public class NoExceptionResponseErrorHandler implements ResponseErrorHandler { + @Override + public boolean hasError(ClientHttpResponse response) throws IOException { + return false; + } + + @Override + public void handleError(ClientHttpResponse response) throws IOException { + + } +} diff --git a/src/main/java/de/financer/controller/template/FinancerRestTemplate.java b/src/main/java/de/financer/controller/template/FinancerRestTemplate.java new file mode 100644 index 0000000..bd4d604 --- /dev/null +++ b/src/main/java/de/financer/controller/template/FinancerRestTemplate.java @@ -0,0 +1,42 @@ +package de.financer.controller.template; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import de.financer.controller.handler.NoExceptionResponseErrorHandler; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.client.RestTemplate; + +public class FinancerRestTemplate { + public ResponseEntity exchange(String url, ParameterizedTypeReference type) { + final RestTemplate restTemplate = new RestTemplate(); + + restTemplate.setErrorHandler(new NoExceptionResponseErrorHandler()); + + return restTemplate.exchange(url, HttpMethod.GET, null, type); + } + + public ResponseEntity exchange(String url, Class type) { + final RestTemplate restTemplate = new RestTemplate(); + + restTemplate.setErrorHandler(new NoExceptionResponseErrorHandler()); + + return restTemplate.exchange(url, HttpMethod.GET, null, type); + } + + // The spring.jackson. properties are not picked up by the RestTemplate and its converter, + // so we need to overwrite it here + private MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { + MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(); + ObjectMapper objectMapper = new ObjectMapper(); + + objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + jsonConverter.setObjectMapper(objectMapper); + + return jsonConverter; + } +} diff --git a/src/main/java/de/financer/controller/template/GetAccountByKeyTemplate.java b/src/main/java/de/financer/controller/template/GetAccountByKeyTemplate.java new file mode 100644 index 0000000..d2552b4 --- /dev/null +++ b/src/main/java/de/financer/controller/template/GetAccountByKeyTemplate.java @@ -0,0 +1,21 @@ +package de.financer.controller.template; + +import de.financer.config.FinancerConfig; +import de.financer.controller.Function; +import de.financer.model.Account; +import de.financer.util.ControllerUtils; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.ResponseEntity; +import org.springframework.web.util.UriComponentsBuilder; + +public class GetAccountByKeyTemplate { + public ResponseEntity exchange(FinancerConfig financerConfig, String key) { + final UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(ControllerUtils + .buildUrl(financerConfig, Function.ACC_GET_BY_KEY)) + .queryParam("key", key); + + return new FinancerRestTemplate() + .exchange(builder.toUriString(), new ParameterizedTypeReference() { + }); + } +} diff --git a/src/main/java/de/financer/controller/template/GetAllAccountsTemplate.java b/src/main/java/de/financer/controller/template/GetAllAccountsTemplate.java new file mode 100644 index 0000000..b1933b5 --- /dev/null +++ b/src/main/java/de/financer/controller/template/GetAllAccountsTemplate.java @@ -0,0 +1,16 @@ +package de.financer.controller.template; + +import de.financer.config.FinancerConfig; +import de.financer.controller.Function; +import de.financer.model.Account; +import de.financer.util.ControllerUtils; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.ResponseEntity; + +public class GetAllAccountsTemplate { + public ResponseEntity> exchange(FinancerConfig financerConfig) { + return new FinancerRestTemplate>().exchange(ControllerUtils + .buildUrl(financerConfig, Function.ACC_GET_ALL), new ParameterizedTypeReference>() { + }); + } +} diff --git a/src/main/java/de/financer/controller/template/GetAllActiveRecurringTransactionsTemplate.java b/src/main/java/de/financer/controller/template/GetAllActiveRecurringTransactionsTemplate.java new file mode 100644 index 0000000..8d779b6 --- /dev/null +++ b/src/main/java/de/financer/controller/template/GetAllActiveRecurringTransactionsTemplate.java @@ -0,0 +1,16 @@ +package de.financer.controller.template; + +import de.financer.config.FinancerConfig; +import de.financer.controller.Function; +import de.financer.model.RecurringTransaction; +import de.financer.util.ControllerUtils; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.ResponseEntity; + +public class GetAllActiveRecurringTransactionsTemplate { + public ResponseEntity> exchange(FinancerConfig financerConfig) { + return new FinancerRestTemplate>().exchange(ControllerUtils + .buildUrl(financerConfig, Function.RT_GET_ALL_ACTIVE), new ParameterizedTypeReference>() { + }); + } +} diff --git a/src/main/java/de/financer/controller/template/GetAllRecurringTransactionsDueTodayTemplate.java b/src/main/java/de/financer/controller/template/GetAllRecurringTransactionsDueTodayTemplate.java new file mode 100644 index 0000000..a63fa03 --- /dev/null +++ b/src/main/java/de/financer/controller/template/GetAllRecurringTransactionsDueTodayTemplate.java @@ -0,0 +1,16 @@ +package de.financer.controller.template; + +import de.financer.config.FinancerConfig; +import de.financer.controller.Function; +import de.financer.model.RecurringTransaction; +import de.financer.util.ControllerUtils; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.ResponseEntity; + +public class GetAllRecurringTransactionsDueTodayTemplate { + public ResponseEntity> exchange(FinancerConfig financerConfig) { + return new FinancerRestTemplate>().exchange(ControllerUtils + .buildUrl(financerConfig, Function.RT_GET_ALL_DUE_TODAY), new ParameterizedTypeReference>() { + }); + } +} diff --git a/src/main/java/de/financer/controller/template/GetAllRecurringTransactionsTemplate.java b/src/main/java/de/financer/controller/template/GetAllRecurringTransactionsTemplate.java new file mode 100644 index 0000000..071bb51 --- /dev/null +++ b/src/main/java/de/financer/controller/template/GetAllRecurringTransactionsTemplate.java @@ -0,0 +1,16 @@ +package de.financer.controller.template; + +import de.financer.config.FinancerConfig; +import de.financer.controller.Function; +import de.financer.model.RecurringTransaction; +import de.financer.util.ControllerUtils; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.ResponseEntity; + +public class GetAllRecurringTransactionsTemplate { + public ResponseEntity> exchange(FinancerConfig financerConfig) { + return new FinancerRestTemplate>().exchange(ControllerUtils + .buildUrl(financerConfig, Function.RT_GET_ALL), new ParameterizedTypeReference>() { + }); + } +} diff --git a/src/main/java/de/financer/controller/template/GetAllTransactionsForAccountTemplate.java b/src/main/java/de/financer/controller/template/GetAllTransactionsForAccountTemplate.java new file mode 100644 index 0000000..8f3b4de --- /dev/null +++ b/src/main/java/de/financer/controller/template/GetAllTransactionsForAccountTemplate.java @@ -0,0 +1,21 @@ +package de.financer.controller.template; + +import de.financer.config.FinancerConfig; +import de.financer.controller.Function; +import de.financer.model.Transaction; +import de.financer.util.ControllerUtils; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.ResponseEntity; +import org.springframework.web.util.UriComponentsBuilder; + +public class GetAllTransactionsForAccountTemplate { + public ResponseEntity> exchange(FinancerConfig financerConfig, String accountKey) { + final UriComponentsBuilder transactionBuilder = UriComponentsBuilder + .fromHttpUrl(ControllerUtils.buildUrl(financerConfig, Function.TR_GET_ALL_FOR_ACCOUNT)) + .queryParam("accountKey", accountKey); + + return new FinancerRestTemplate>() + .exchange(transactionBuilder.toUriString(), new ParameterizedTypeReference>() { + }); + } +} diff --git a/src/main/java/de/financer/controller/template/StringTemplate.java b/src/main/java/de/financer/controller/template/StringTemplate.java new file mode 100644 index 0000000..96148e0 --- /dev/null +++ b/src/main/java/de/financer/controller/template/StringTemplate.java @@ -0,0 +1,13 @@ +package de.financer.controller.template; + +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.ResponseEntity; +import org.springframework.web.util.UriComponentsBuilder; + +public class StringTemplate { + public ResponseEntity exchange(UriComponentsBuilder builder) { + return new FinancerRestTemplate() + .exchange(builder.toUriString(), new ParameterizedTypeReference() { + }); + } +} diff --git a/src/main/java/de/financer/form/NewAccountForm.java b/src/main/java/de/financer/form/NewAccountForm.java new file mode 100644 index 0000000..fd3218d --- /dev/null +++ b/src/main/java/de/financer/form/NewAccountForm.java @@ -0,0 +1,22 @@ +package de.financer.form; + +public class NewAccountForm { + private String key; + private String type; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/src/main/java/de/financer/form/NewRecurringTransactionForm.java b/src/main/java/de/financer/form/NewRecurringTransactionForm.java new file mode 100644 index 0000000..3f7d419 --- /dev/null +++ b/src/main/java/de/financer/form/NewRecurringTransactionForm.java @@ -0,0 +1,76 @@ +package de.financer.form; + +public class NewRecurringTransactionForm { + private String fromAccountKey; + private String toAccountKey; + private String amount; + private String description; + private String firstOccurrence; + private String lastOccurrence; + private String intervalType; + private String holidayWeekendType; + + public String getFromAccountKey() { + return fromAccountKey; + } + + public void setFromAccountKey(String fromAccountKey) { + this.fromAccountKey = fromAccountKey; + } + + public String getToAccountKey() { + return toAccountKey; + } + + public void setToAccountKey(String toAccountKey) { + this.toAccountKey = toAccountKey; + } + + public String getAmount() { + return amount; + } + + public void setAmount(String amount) { + this.amount = amount; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getFirstOccurrence() { + return firstOccurrence; + } + + public void setFirstOccurrence(String firstOccurrence) { + this.firstOccurrence = firstOccurrence; + } + + public String getLastOccurrence() { + return lastOccurrence; + } + + public void setLastOccurrence(String lastOccurrence) { + this.lastOccurrence = lastOccurrence; + } + + public String getIntervalType() { + return intervalType; + } + + public void setIntervalType(String intervalType) { + this.intervalType = intervalType; + } + + public String getHolidayWeekendType() { + return holidayWeekendType; + } + + public void setHolidayWeekendType(String holidayWeekendType) { + this.holidayWeekendType = holidayWeekendType; + } +} diff --git a/src/main/java/de/financer/form/NewTransactionForm.java b/src/main/java/de/financer/form/NewTransactionForm.java new file mode 100644 index 0000000..89dcd6b --- /dev/null +++ b/src/main/java/de/financer/form/NewTransactionForm.java @@ -0,0 +1,49 @@ +package de.financer.form; + +public class NewTransactionForm { + private String fromAccountKey; + private String toAccountKey; + private String amount; + private String date; + private String description; + + public String getFromAccountKey() { + return fromAccountKey; + } + + public void setFromAccountKey(String fromAccountKey) { + this.fromAccountKey = fromAccountKey; + } + + public String getToAccountKey() { + return toAccountKey; + } + + public void setToAccountKey(String toAccountKey) { + this.toAccountKey = toAccountKey; + } + + public String getAmount() { + return amount; + } + + public void setAmount(String amount) { + this.amount = amount; + } + + public String getDate() { + return date; + } + + public void setDate(String date) { + this.date = date; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/src/main/java/de/financer/model/Account.java b/src/main/java/de/financer/model/Account.java new file mode 100644 index 0000000..0e4101e --- /dev/null +++ b/src/main/java/de/financer/model/Account.java @@ -0,0 +1,45 @@ +package de.financer.model; + +public class Account { + private Long id; + private String key; + private AccountType type; + private AccountStatus status; + private Long currentBalance; + + public Long getId() { + return id; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public AccountType getType() { + return type; + } + + public void setType(AccountType type) { + this.type = type; + } + + public AccountStatus getStatus() { + return status; + } + + public void setStatus(AccountStatus status) { + this.status = status; + } + + public Long getCurrentBalance() { + return currentBalance; + } + + public void setCurrentBalance(Long currentBalance) { + this.currentBalance = currentBalance; + } +} diff --git a/src/main/java/de/financer/model/AccountStatus.java b/src/main/java/de/financer/model/AccountStatus.java new file mode 100644 index 0000000..9298e31 --- /dev/null +++ b/src/main/java/de/financer/model/AccountStatus.java @@ -0,0 +1,20 @@ +package de.financer.model; + +import java.util.Arrays; + +public enum AccountStatus { + /** Indicates that the account is open for bookings */ + OPEN, + /** Indicates that the account is closed and bookings to it are forbidden */ + CLOSED; + + /** + * This method validates whether the given string represents a valid account status. + * + * @param status to check + * @return whether the given status represents a valid account status + */ + public static boolean isValidType(String status) { + return Arrays.stream(AccountStatus.values()).anyMatch((accountStatus) -> accountStatus.name().equals(status)); + } +} diff --git a/src/main/java/de/financer/model/AccountType.java b/src/main/java/de/financer/model/AccountType.java new file mode 100644 index 0000000..763d398 --- /dev/null +++ b/src/main/java/de/financer/model/AccountType.java @@ -0,0 +1,38 @@ +package de.financer.model; + +import java.util.*; +import java.util.stream.Collectors; + +public enum AccountType { + /** Used to mark an account that acts as a source of money, e.g. monthly wage */ + INCOME, + + /** Indicates a real account at a bank, e.g. a check payment account */ + BANK, + + /** Marks an account as physical cash, e.g. the money currently in the purse */ + CASH, + + /** Used to mark an account that acts as a destination of money, e.g. through buying goods */ + EXPENSE, + + /** Marks an account as a liability from a third party, e.g. credit card or loan */ + LIABILITY, + + /** Marks the start account that is to be used to book all the opening balances for the different accounts */ + START; + + /** + * This method validates whether the given string represents a valid account type. + * + * @param type to check + * @return whether the given type represents a valid account type + */ + public static boolean isValidType(String type) { + return Arrays.stream(AccountType.values()).anyMatch((accountType) -> accountType.name().equals(type)); + } + + public static List valueList() { + return Arrays.stream(AccountType.values()).map(AccountType::name).collect(Collectors.toList()); + } +} diff --git a/src/main/java/de/financer/model/HolidayWeekendType.java b/src/main/java/de/financer/model/HolidayWeekendType.java new file mode 100644 index 0000000..6b79ab6 --- /dev/null +++ b/src/main/java/de/financer/model/HolidayWeekendType.java @@ -0,0 +1,71 @@ +package de.financer.model; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * This enum specifies constants that control how actions should be handled that would fall on a holiday + * or weekday (where usually are no bookings done by e.g. banks) + */ +public enum HolidayWeekendType { + /** Indicates that the action should be done on the specified day regardless whether it's a holiday or a weekend */ + SAME_DAY, + + /** + *

+ * Indicates that the action should be deferred to the next workday. + *

+ *
+     *     Example 1:
+     *     MO   TU   WE   TH   FR   SA   SO
+     *               H              WE   WE   -> Holiday/WeekEnd
+     *               X                        -> Due date of action
+     *                    X'                  -> Deferred, effective due date of action
+     * 
+ *
+     *     Example 2:
+     *     TU   WE   TH   FR   SA   SO   MO
+     *          H              WE   WE        -> Holiday/WeekEnd
+     *                         X              -> Due date of action
+     *                                   X'   -> Deferred, effective due date of action
+     * 
+ * + */ + NEXT_WORKDAY, + + /** + *

+ * Indicates that the action should preponed to the previous day + *

+ *
+     *     Example 1:
+     *     MO   TU   WE   TH   FR   SA   SO
+     *               H              WE   WE   -> Holiday/WeekEnd
+     *               X                        -> Due date of action
+     *          X'                            -> Earlier, effective due date of action
+     * 
+ *
+     *     Example 2:
+     *     MO   TU   WE   TH   FR   SA   SO
+     *                         H    WE   WE   -> Holiday/WeekEnd
+     *                                   X    -> Due date of action
+     *                    X'                  -> Earlier, effective due date of action
+     * 
+ */ + PREVIOUS_WORKDAY; + + /** + * This method validates whether the given string represents a valid holiday weekend type. + * + * @param type to check + * @return whether the given type represents a valid holiday weekend type + */ + public static boolean isValidType(String type) { + return Arrays.stream(HolidayWeekendType.values()).anyMatch((holidayWeekendType) -> holidayWeekendType.name().equals(type)); + } + + public static List valueList() { + return Arrays.stream(HolidayWeekendType.values()).map(HolidayWeekendType::name).collect(Collectors.toList()); + } +} diff --git a/src/main/java/de/financer/model/IntervalType.java b/src/main/java/de/financer/model/IntervalType.java new file mode 100644 index 0000000..180d9ef --- /dev/null +++ b/src/main/java/de/financer/model/IntervalType.java @@ -0,0 +1,36 @@ +package de.financer.model; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public enum IntervalType { + /** Indicates that an action should be executed every day */ + DAILY, + + /** Indicates that an action should be executed once a week */ + WEEKLY, + + /** Indicates that an action should be executed once a month */ + MONTHLY, + + /** Indicates that an action should be executed once a quarter */ + QUARTERLY, + + /** Indicates that an action should be executed once a year */ + YEARLY; + + /** + * This method validates whether the given string represents a valid interval type. + * + * @param type to check + * @return whether the given type represents a valid interval type + */ + public static boolean isValidType(String type) { + return Arrays.stream(IntervalType.values()).anyMatch((intervalType) -> intervalType.name().equals(type)); + } + + public static List valueList() { + return Arrays.stream(IntervalType.values()).map(IntervalType::name).collect(Collectors.toList()); + } +} diff --git a/src/main/java/de/financer/model/RecurringTransaction.java b/src/main/java/de/financer/model/RecurringTransaction.java new file mode 100644 index 0000000..417596f --- /dev/null +++ b/src/main/java/de/financer/model/RecurringTransaction.java @@ -0,0 +1,83 @@ +package de.financer.model; + +import java.time.LocalDate; + +public class RecurringTransaction { + private Long id; + private Account fromAccount; + private Account toAccount; + private String description; + private Long amount; + private IntervalType intervalType; + private LocalDate firstOccurrence; + private LocalDate lastOccurrence; + private HolidayWeekendType holidayWeekendType; + + public Long getId() { + return id; + } + + public Account getFromAccount() { + return fromAccount; + } + + public void setFromAccount(Account fromAccount) { + this.fromAccount = fromAccount; + } + + public Account getToAccount() { + return toAccount; + } + + public void setToAccount(Account toAccount) { + this.toAccount = toAccount; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Long getAmount() { + return amount; + } + + public void setAmount(Long amount) { + this.amount = amount; + } + + public HolidayWeekendType getHolidayWeekendType() { + return holidayWeekendType; + } + + public void setHolidayWeekendType(HolidayWeekendType holidayWeekendType) { + this.holidayWeekendType = holidayWeekendType; + } + + public LocalDate getLastOccurrence() { + return lastOccurrence; + } + + public void setLastOccurrence(LocalDate lastOccurrence) { + this.lastOccurrence = lastOccurrence; + } + + public LocalDate getFirstOccurrence() { + return firstOccurrence; + } + + public void setFirstOccurrence(LocalDate firstOccurrence) { + this.firstOccurrence = firstOccurrence; + } + + public IntervalType getIntervalType() { + return intervalType; + } + + public void setIntervalType(IntervalType intervalType) { + this.intervalType = intervalType; + } +} diff --git a/src/main/java/de/financer/model/Transaction.java b/src/main/java/de/financer/model/Transaction.java new file mode 100644 index 0000000..16c848d --- /dev/null +++ b/src/main/java/de/financer/model/Transaction.java @@ -0,0 +1,65 @@ +package de.financer.model; + +import java.time.LocalDate; + +public class Transaction { + private Long id; + private Account fromAccount; + private Account toAccount; + private LocalDate date; + private String description; + private Long amount; + private RecurringTransaction recurringTransaction; + + public Long getId() { + return id; + } + + public Account getFromAccount() { + return fromAccount; + } + + public void setFromAccount(Account fromAccount) { + this.fromAccount = fromAccount; + } + + public Account getToAccount() { + return toAccount; + } + + public void setToAccount(Account toAccount) { + this.toAccount = toAccount; + } + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Long getAmount() { + return amount; + } + + public void setAmount(Long amount) { + this.amount = amount; + } + + public RecurringTransaction getRecurringTransaction() { + return recurringTransaction; + } + + public void setRecurringTransaction(RecurringTransaction recurringTransaction) { + this.recurringTransaction = recurringTransaction; + } +} diff --git a/src/main/java/de/financer/util/ControllerUtils.java b/src/main/java/de/financer/util/ControllerUtils.java new file mode 100644 index 0000000..b9debf2 --- /dev/null +++ b/src/main/java/de/financer/util/ControllerUtils.java @@ -0,0 +1,50 @@ +package de.financer.util; + +import de.financer.config.FinancerConfig; +import de.financer.controller.Function; +import de.financer.model.Account; +import de.financer.model.AccountStatus; +import de.financer.model.RecurringTransaction; +import de.financer.util.comparator.AccountByTypeComparator; +import org.apache.commons.collections4.IterableUtils; +import org.apache.commons.lang3.StringUtils; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.stream.Collectors; + +public class ControllerUtils { + public static String buildUrl(FinancerConfig financerConfig, Function function) { + return String.format("%s%s", financerConfig.getServerUrl(), function.getPath()); + } + + public static List filterAndSortAccounts(Iterable accounts) { + return filterAndSortAccounts(accounts, false); + } + + public static List filterAndSortAccounts(Iterable accounts, boolean showClosed) { + return IterableUtils.toList(accounts).stream() + .filter((acc) -> AccountStatus.OPEN + .equals(acc.getStatus()) || showClosed) + .sorted(new AccountByTypeComparator()) + .collect(Collectors.toList()); + } + + public static String formatDate(FinancerConfig financerConfig, String dateFromForm) { + if (StringUtils.isEmpty(dateFromForm)) { + return null; + } + + // The format is always "yyyy-MM-dd", see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date + final LocalDate formatted = LocalDate.parse(dateFromForm, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + + return formatted.format(DateTimeFormatter.ofPattern(financerConfig.getDateFormat())); + } + + public static List filterEmptyEntries(Iterable recurringTransactions) { + return IterableUtils.toList(recurringTransactions).stream() + .filter((rt) -> rt.getFromAccount() != null && rt.getToAccount() != null) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/de/financer/util/comparator/AccountByTypeComparator.java b/src/main/java/de/financer/util/comparator/AccountByTypeComparator.java new file mode 100644 index 0000000..4723d9c --- /dev/null +++ b/src/main/java/de/financer/util/comparator/AccountByTypeComparator.java @@ -0,0 +1,23 @@ +package de.financer.util.comparator; + +import de.financer.model.Account; +import de.financer.model.AccountType; + +import java.util.Comparator; +import java.util.Map; + +public class AccountByTypeComparator implements Comparator { + //@formatter:off + private static final Map SORT_ORDER = Map.of(AccountType.BANK, 1, + AccountType.CASH, 2, + AccountType.INCOME, 3, + AccountType.LIABILITY, 4, + AccountType.EXPENSE, 5, + AccountType.START, 6); + //@formatter:on + + @Override + public int compare(Account o1, Account o2) { + return SORT_ORDER.get(o1.getType()).compareTo(SORT_ORDER.get(o2.getType())); + } +} diff --git a/src/main/resources/config/application-dev.properties b/src/main/resources/config/application-dev.properties new file mode 100644 index 0000000..000f956 --- /dev/null +++ b/src/main/resources/config/application-dev.properties @@ -0,0 +1,2 @@ +# Hibernate +spring.jpa.show-sql=true \ No newline at end of file diff --git a/src/main/resources/config/application.properties b/src/main/resources/config/application.properties new file mode 100644 index 0000000..3da6796 --- /dev/null +++ b/src/main/resources/config/application.properties @@ -0,0 +1,25 @@ +### +### This is the main configuration file of the application. +### Filtering of the @...@ values happens via the maven-resource-plugin. The execution of the plugin is configured in +### the Spring Boot parent POM. + +spring.profiles.active=@activeProfiles@ + +server.servlet.context-path=/financer-web-client +server.port=8090 + +info.app.name=Financer Web Client +info.app.description=A web interface for the financer server application +info.build.group=@project.groupId@ +info.build.artifact=@project.artifactId@ +info.build.version=@project.version@ + +logging.level.de.financer=DEBUG +logging.file=financer-web-client.log + +# The date format of the client-supplied date string, used to parse the string into a proper object +financer.dateFormat=dd.MM.yyyy + +financer.serverUrl=http://localhost:8089/financer-server/ + +spring.messages.basename=i18n/message \ No newline at end of file diff --git a/src/main/resources/i18n/message.properties b/src/main/resources/i18n/message.properties new file mode 100644 index 0000000..b2adb1f --- /dev/null +++ b/src/main/resources/i18n/message.properties @@ -0,0 +1,142 @@ +financer.account-overview.title=financer\: overview +financer.account-overview.available-actions=Available actions\: +financer.account-overview.available-actions.show-closed=Show closed accounts +financer.account-overview.available-actions.hide-closed=Hide closed accounts +financer.account-overview.available-actions.create-account=Create new account +financer.account-overview.available-actions.create-transaction=Create new transaction +financer.account-overview.available-actions.create-recurring-transaction=Create new recurring transaction +financer.account-overview.available-actions.recurring-transaction-all=Show all recurring transactions +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\: +financer.account-overview.table-header.id=ID +financer.account-overview.table-header.key=Key +financer.account-overview.table-header.balance=Current Balance +financer.account-overview.table-header.type=Type +financer.account-overview.table-header.status=Status + +financer.account-new.title=financer\: create new account +financer.account-new.label.key=Key\: +financer.account-new.label.type=Type\: +financer.account-new.submit=Create account + +financer.transaction-new.title=financer\: create new transaction +financer.transaction-new.label.from-account=From account\: +financer.transaction-new.label.to-account=To account\: +financer.transaction-new.label.amount=Amount\: +financer.transaction-new.label.date=Date\: +financer.transaction-new.label.description=Description\: +financer.transaction-new.submit=Create transaction +financer.transaction-new.account-type.BANK={0}|Bank|{1} +financer.transaction-new.account-type.CASH={0}|Cash|{1} +financer.transaction-new.account-type.INCOME={0}|Income|{1} +financer.transaction-new.account-type.LIABILITY={0}|Liability|{1} +financer.transaction-new.account-type.EXPENSE={0}|Expense|{1} +financer.transaction-new.account-type.START={0}|Start|{1} + +financer.recurring-transaction-new.title=financer\: create new recurring transaction +financer.recurring-transaction-new.label.from-account=From account\: +financer.recurring-transaction-new.label.to-account=To account\: +financer.recurring-transaction-new.label.amount=Amount\: +financer.recurring-transaction-new.label.first-occurrence=First occurrence\: +financer.recurring-transaction-new.label.last-occurrence=Last occurrence\: +financer.recurring-transaction-new.label.interval-type=Interval\: +financer.recurring-transaction-new.label.holiday-weekend-type=Holiday/weekend rule\: +financer.recurring-transaction-new.label.description=Description\: +financer.recurring-transaction-new.submit=Create recurring transaction +financer.recurring-transaction-new.account-type.BANK={0}|Bank|{1} +financer.recurring-transaction-new.account-type.CASH={0}|Cash|{1} +financer.recurring-transaction-new.account-type.INCOME={0}|Income|{1} +financer.recurring-transaction-new.account-type.LIABILITY={0}|Liability|{1} +financer.recurring-transaction-new.account-type.EXPENSE={0}|Expense|{1} +financer.recurring-transaction-new.account-type.START={0}|Start|{1} + +financer.recurring-transaction-list.title.dueToday=financer\: recurring transactions due today +financer.recurring-transaction-list.title.active=financer\: active recurring transactions +financer.recurring-transaction-list.title.all=financer\: all recurring transaction +financer.recurring-transaction-list.table-header.id=ID +financer.recurring-transaction-list.table-header.fromAccount=From account +financer.recurring-transaction-list.table-header.toAccount=To account +financer.recurring-transaction-list.table-header.firstOccurrence=First occurrence +financer.recurring-transaction-list.table-header.lastOccurrence=Last occurrence +financer.recurring-transaction-list.table-header.amount=Amount +financer.recurring-transaction-list.table-header.description=Description +financer.recurring-transaction-list.table-header.intervalType=Interval +financer.recurring-transaction-list.table-header.holidayWeekendType=Holiday/weekend rule +financer.recurring-transaction-list.table-header.actions=Actions +financer.recurring-transaction-list.table.actions.createTransaction=Create transaction +financer.recurring-transaction-list.table.actions.createTransactionWithAmount=Create transaction with amount +financer.recurring-transaction-list.table.actions.editRecurringTransaction=Edit +financer.recurring-transaction-list.table.actions.deleteRecurringTransaction=Delete + +financer.account-details.title=financer\: account details +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.table-header.id=ID +financer.account-details.table-header.fromAccount=From account +financer.account-details.table-header.toAccount=To account +financer.account-details.table-header.date=Date +financer.account-details.table-header.amount=Amount +financer.account-details.table-header.description=Description +financer.account-details.table-header.byRecurring=Recurring? +financer.account-details.details.type=Type\: +financer.account-details.details.balance=Current Balance\: +financer.account-details.table-header.actions=Actions +financer.account-details.table.actions.editTransaction=Edit +financer.account-details.table.actions.deleteTransaction=Delete + +financer.interval-type.DAILY=Daily +financer.interval-type.WEEKLY=Weekly +financer.interval-type.MONTHLY=Monthly +financer.interval-type.QUARTERLY=Quarterly +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.account-type.BANK=Bank +financer.account-type.CASH=Cash +financer.account-type.INCOME=Income +financer.account-type.LIABILITY=Liability +financer.account-type.EXPENSE=Expense +financer.account-type.START=Start + +financer.account-status.OPEN=Open +financer.account-status.CLOSED=Closed + +financer.heading.transaction-new=financer\: create new transaction +financer.heading.recurring-transaction-new=financer\: create new recurring transaction +financer.heading.account-new=financer\: create new account +financer.heading.account-overview=financer\: overview +financer.heading.account-details=financer\: account details for {0} +financer.heading.recurring-transaction-list.dueToday=financer\: recurring transactions due today +financer.heading.recurring-transaction-list.active=financer\: active recurring transactions +financer.heading.recurring-transaction-list.all=financer\: all recurring transaction + +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.INVALID_ACCOUNT_KEY=The entered account key is invalid! It has to start with 'accounts.'. +financer.error-message.FROM_ACCOUNT_NOT_FOUND=The specified from account has not been found! +financer.error-message.TO_ACCOUNT_NOT_FOUND=The specified to account has not been found! +financer.error-message.FROM_AND_TO_ACCOUNT_NOT_FOUND=Neither from nor to have not been found! +financer.error-message.INVALID_DATE_FORMAT=The date entered has the wrong format! +financer.error-message.MISSING_DATE=No date entered! +financer.error-message.AMOUNT_ZERO=Zero is not a valid booking amount! +financer.error-message.MISSING_AMOUNT=No amount entered! +financer.error-message.INVALID_BOOKING_ACCOUNTS=The booking is not valid! No booking allowed from the from account type to the to account type! +financer.error-message.MISSING_HOLIDAY_WEEKEND_TYPE=No holiday weekend type entered! +financer.error-message.INVALID_HOLIDAY_WEEKEND_TYPE=The holiday weekend type is not valid! +financer.error-message.MISSING_INTERVAL_TYPE=No interval type entered! +financer.error-message.INVALID_INTERVAL_TYPE=The interval type is not valid! +financer.error-message.MISSING_FIRST_OCCURRENCE=No first occurrence entered! +financer.error-message.INVALID_FIRST_OCCURRENCE_FORMAT=The date format of the first occurrence is invalid! +financer.error-message.INVALID_LAST_OCCURRENCE_FORMAT=The date format of the last occurrence is invalid! +financer.error-message.MISSING_RECURRING_TRANSACTION_ID=No recurring transaction entered! +financer.error-message.INVALID_RECURRING_TRANSACTION_ID=The recurring transaction is not valid! +financer.error-message.RECURRING_TRANSACTION_NOT_FOUND=The recurring transaction could not be found! +financer.error-message.MISSING_TRANSACTION_ID=No transaction entered! +financer.error-message.INVALID_TRANSACTION_ID=The transaction is not valid! +financer.error-message.TRANSACTION_NOT_FOUND=The transaction could not be found! +financer.error-message.ACCOUNT_NOT_FOUND=The account could not be found! \ No newline at end of file diff --git a/src/main/resources/i18n/message_de_DE.properties b/src/main/resources/i18n/message_de_DE.properties new file mode 100644 index 0000000..045cf67 --- /dev/null +++ b/src/main/resources/i18n/message_de_DE.properties @@ -0,0 +1,116 @@ +financer.account-overview.title=financer\: \u00DCbersicht +financer.account-overview.available-actions=Verf\u00FCgbare Aktionen\: +financer.account-overview.available-actions.show-closed=Zeige auch geschlossene Konten +financer.account-overview.available-actions.hide-closed=Verstecke geschlossene Konten +financer.account-overview.available-actions.create-account=Neues Konto erstellen +financer.account-overview.available-actions.create-transaction=Neue Buchung erstellen +financer.account-overview.available-actions.create-recurring-transaction=Neue wiederkehrende Buchung erstellen +financer.account-overview.available-actions.recurring-transaction-all=Zeige alle wiederkehrende Buchungen +financer.account-overview.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\: +financer.account-overview.table-header.id=ID +financer.account-overview.table-header.key=Schl\u00FCssel +financer.account-overview.table-header.balance=Kontostand +financer.account-overview.table-header.type=Typ +financer.account-overview.table-header.status=Status + +financer.account-new.title=financer\: Neues Konto erstellen +financer.account-new.label.key=Schl\u00FCssel\: +financer.account-new.label.type=Typ\: +financer.account-new.submit=Account erstellen + +financer.transaction-new.title=financer\: Neue Buchung erstellen +financer.transaction-new.label.from-account=Von Konto\: +financer.transaction-new.label.to-account=An Konto\: +financer.transaction-new.label.amount=Betrag\: +financer.transaction-new.label.date=Datum\: +financer.transaction-new.label.description=Beschreibung\: +financer.transaction-new.submit=Buchung erstellen +financer.transaction-new.account-type.BANK={0}|Bank|{1} +financer.transaction-new.account-type.CASH={0}|Bar|{1} +financer.transaction-new.account-type.INCOME={0}|Einkommen|{1} +financer.transaction-new.account-type.LIABILITY={0}|Verbindlichkeit|{1} +financer.transaction-new.account-type.EXPENSE={0}|Aufwand|{1} +financer.transaction-new.account-type.START={0}|Anfangsbestand|{1} + +financer.recurring-transaction-new.title=financer\: Neue wiederkehrende Buchung erstellen +financer.recurring-transaction-new.label.from-account=Von Konto\: +financer.recurring-transaction-new.label.to-account=An Konto\: +financer.recurring-transaction-new.label.amount=Betrag\: +financer.recurring-transaction-new.label.first-occurrence=Erstes Auftreten\: +financer.recurring-transaction-new.label.last-occurrence=Letztes Auftreten\: +financer.recurring-transaction-new.label.interval-type=Intervall\: +financer.recurring-transaction-new.label.holiday-weekend-type=Feiertag-/Wochenendregel\: +financer.recurring-transaction-new.label.description=Beschreibung\: +financer.recurring-transaction-new.submit=Wiederkehrende Buchung erstellen +financer.recurring-transaction-new.account-type.BANK={0}|Bank|{1} +financer.recurring-transaction-new.account-type.CASH={0}|Bar|{1} +financer.recurring-transaction-new.account-type.INCOME={0}|Einkommen|{1} +financer.recurring-transaction-new.account-type.LIABILITY={0}|Verbindlichkeit|{1} +financer.recurring-transaction-new.account-type.EXPENSE={0}|Aufwand|{1} +financer.recurring-transaction-new.account-type.START={0}|Anfangsbestand|{1} + +financer.recurring-transaction-list.title.dueToday=financer\: wiederkehrende Buchungen heute f\u00E4llig +financer.recurring-transaction-list.title.active=financer\: aktive wiederkehrende Buchungen +financer.recurring-transaction-list.title.all=financer\: alle wiederkehrende Buchungen +financer.recurring-transaction-list.table-header.id=ID +financer.recurring-transaction-list.table-header.fromAccount=Von Konto +financer.recurring-transaction-list.table-header.toAccount=An Konto +financer.recurring-transaction-list.table-header.firstOccurrence=Erstes Auftreten +financer.recurring-transaction-list.table-header.lastOccurrence=Letztes Auftreten +financer.recurring-transaction-list.table-header.amount=Betrag +financer.recurring-transaction-list.table-header.description=Beschreibung +financer.recurring-transaction-list.table-header.intervalType=Intervall +financer.recurring-transaction-list.table-header.holidayWeekendType=Feiertag-/Wochenendregel +financer.recurring-transaction-list.table-header.actions=Aktionen +financer.recurring-transaction-list.table.actions.createTransaction=Erstelle Buchung +financer.recurring-transaction-list.table.actions.createTransactionWithAmount=Erstelle Buchung mit Betrag +financer.recurring-transaction-list.table.actions.editRecurringTransaction=Bearbeiten +financer.recurring-transaction-list.table.actions.deleteRecurringTransaction=Löschen + +financer.account-details.title=financer\: Kontodetails +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.table-header.id=ID +financer.account-details.table-header.fromAccount=Von Konto +financer.account-details.table-header.toAccount=An Konto +financer.account-details.table-header.date=Datum +financer.account-details.table-header.amount=Betrag +financer.account-details.table-header.description=Beschreibung +financer.account-details.table-header.byRecurring=Wiederkehrend? +financer.account-details.details.type=Typ\: +financer.account-details.details.balance=Kontostand\: +financer.account-details.table-header.actions=Aktionen +financer.account-details.table.actions.editTransaction=Bearbeiten +financer.account-details.table.actions.deleteTransaction=Löschen + +financer.interval-type.DAILY=T\u00E4glich +financer.interval-type.WEEKLY=W\u00F6chentlich +financer.interval-type.MONTHLY=Monatlich +financer.interval-type.QUARTERLY=Viertelj\u00E4hrlich +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.account-type.BANK=Bank +financer.account-type.CASH=Bar +financer.account-type.INCOME=Einkommen +financer.account-type.LIABILITY=Verbindlichkeit +financer.account-type.EXPENSE=Aufwand +financer.account-type.START=Anfangsbestand + +financer.account-status.OPEN=Offen +financer.account-status.CLOSED=Geschlossen + +financer.heading.transaction-new=financer\: Neue Buchung erstellen +financer.heading.recurring-transaction-new=financer\: Neue wiederkehrende Buchung erstellen +financer.heading.account-new=financer\: Neues Konto erstellen +financer.heading.account-overview=financer\: \u00DCbersicht +financer.heading.account-details=financer\: Kontodetails f\u00FCr {0} +financer.heading.recurring-transaction-list.dueToday=financer\: wiederkehrende Buchungen heute f\u00E4llig +financer.heading.recurring-transaction-list.active=financer\: aktive wiederkehrende Buchungen +financer.heading.recurring-transaction-list.all=financer\: alle wiederkehrende Buchungen \ No newline at end of file diff --git a/src/main/resources/static/css/main.css b/src/main/resources/static/css/main.css new file mode 100644 index 0000000..eb6b2d9 --- /dev/null +++ b/src/main/resources/static/css/main.css @@ -0,0 +1,67 @@ +#account-overview-table, +#account-transaction-table, +#recurring-transaction-list-table { + width: 100%; + border-collapse: collapse; + text-align: left; + margin-top: 1.5em; + margin-bottom: 1.5em; +} + +#account-overview-table th, +#account-overview-table td, +#account-transaction-table th, +#account-transaction-table td, +#recurring-transaction-list-table th, +#recurring-transaction-list-table td { + border-bottom: 1px solid #ddd; + padding: 0.3em; + vertical-align: top; +} + +tr:hover { + background-color: lightgrey; +} + +@media only screen and (max-width: 450px) { + .hideable-column { + display: none; + } + #new-account-form *, + #new-transaction-form *, + #new-recurring-transaction-form * { + width: 100% !important; + } +} + +#action-container *, +#recurring-transaction-list-table-actions-container *, +#account-transaction-table-actions-container * { + display: block; +} + +#details-container, +#status-container { + margin-bottom: 1em; +} + +#status-container > span, div { + display: block; +} + +#details-container > div { + display: block; +} + +#new-account-form *, +#new-transaction-form *, +#new-recurring-transaction-form * { + display: block; + margin-top: 1em; + width: 20em; + box-sizing: border-box; +} + +.errorMessage { + color: #ff6666 +} \ No newline at end of file diff --git a/src/main/resources/templates/account/accountDetails.html b/src/main/resources/templates/account/accountDetails.html new file mode 100644 index 0000000..e721517 --- /dev/null +++ b/src/main/resources/templates/account/accountDetails.html @@ -0,0 +1,59 @@ + + + + + <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}"> +</head> +<body> +<h1 th:text="#{financer.heading.account-details(${account.key})}" /> +<span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/> +<div id="details-container"> + <div id="type-container"> + <span th:text="#{financer.account-details.details.type}"/> + <span th:text="#{'financer.account-type.' + ${account.type}}"/> + </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')}"/> + </div> +</div> +<div id="action-container"> + <span th:text="#{financer.account-details.available-actions}"/> + <a th:if="${!isClosed}" th:href="@{/closeAccount(key=${account.key})}" + 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}"/> +</div> +<table id="account-transaction-table"> + <tr> + <th class="hideable-column" th:text="#{financer.account-details.table-header.id}"/> + <th th:text="#{financer.account-details.table-header.fromAccount}"/> + <th th:text="#{financer.account-details.table-header.toAccount}"/> + <th th:text="#{financer.account-details.table-header.date}"/> + <th th:text="#{financer.account-details.table-header.amount}"/> + <th th:text="#{financer.account-details.table-header.description}"/> + <th th:text="#{financer.account-details.table-header.byRecurring}"/> + <th th:text="#{financer.account-details.table-header.actions}"/> + </tr> + <tr th:each="transaction : ${transactions}"> + <td class="hideable-column" th:text="${transaction.id}"/> + <td th:text="${transaction.fromAccount.key}" /> + <td th:text="${transaction.toAccount.key}" /> + <td th:text="${transaction.date}" /> + <td th:text="${#numbers.formatDecimal(transaction.amount/100D, 1, 'DEFAULT', 2, 'DEFAULT')}"/> + <td th:text="${transaction.description}" /> + <td th:text="${transaction.recurringTransaction != null}" /> + <td> + <div id="account-transaction-table-actions-container"> + <a th:href="@{/editTransaction}" + th:text="#{financer.account-details.table.actions.editTransaction}"/> + <a th:href="@{/deleteTransaction}" + th:text="#{financer.account-details.table.actions.deleteTransaction}"/> + </div> + </td> + </tr> +</table> +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/account/accountOverview.html b/src/main/resources/templates/account/accountOverview.html new file mode 100644 index 0000000..559e24d --- /dev/null +++ b/src/main/resources/templates/account/accountOverview.html @@ -0,0 +1,54 @@ +<!DOCTYPE HTML> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <title th:text="#{financer.account-overview.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}"> +</head> +<body> +<h1 th:text="#{financer.heading.account-overview}" /> +<div id="status-container"> + <span th:text="#{financer.account-overview.status}"/> + <div> + <span th:text="#{financer.account-overview.status.recurring-transaction-due-today}"/> + <a th:href="@{/recurringTransactionDueToday}" th:text="${rtDueTodayCount}"/> + </div> + <div> + <span th:text="#{financer.account-overview.status.recurring-transaction-active}"/> + <a th:href="@{/recurringTransactionActive}" th:text="${rtAllActiveCount}"/> + </div> +</div> +<div id="action-container"> + <span th:text="#{financer.account-overview.available-actions}"/> + <a th:if="${!showClosed}" th:href="@{'/accountOverview?showClosed=true'}" + th:text="#{financer.account-overview.available-actions.show-closed}"/> + <a th:if="${showClosed}" th:href="@{'/accountOverview'}" + th:text="#{financer.account-overview.available-actions.hide-closed}"/> + <a th:href="@{/newAccount}" th:text="#{financer.account-overview.available-actions.create-account}"/> + <a th:href="@{/newTransaction}" th:text="#{financer.account-overview.available-actions.create-transaction}"/> + <a th:href="@{/newRecurringTransaction}" + th:text="#{financer.account-overview.available-actions.create-recurring-transaction}"/> + <a th:href="@{/recurringTransactionAll}" + th:text="#{financer.account-overview.available-actions.recurring-transaction-all}"/> +</div> +<table id="account-overview-table"> + <tr> + <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.type}"/> + <th class="hideable-column" th:text="#{financer.account-overview.table-header.status}"/> + </tr> + <tr th:each="acc : ${accounts}"> + <td class="hideable-column" th:text="${acc.id}"/> + <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')}"/> + <td class="hideable-column" th:text="#{'financer.account-type.' + ${acc.type}}"/> + <td class="hideable-column" th:text="#{'financer.account-status.' + ${acc.status}}"/> + </tr> +</table> +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/account/newAccount.html b/src/main/resources/templates/account/newAccount.html new file mode 100644 index 0000000..c046626 --- /dev/null +++ b/src/main/resources/templates/account/newAccount.html @@ -0,0 +1,22 @@ +<!DOCTYPE HTML> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <title th:text="#{financer.account-new.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}"> +</head> +<body> +<h1 th:text="#{financer.heading.account-new}" /> +<span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/> +<form id="new-account-form" action="#" th:action="@{/saveAccount}" th:object="${newAccountForm}" method="post"> + <label for="inputKey" th:text="#{financer.account-new.label.key}"/> + <input type="text" id="inputKey" th:field="*{key}" placeholder="accounts."/> + <label for="selectType" th:text="#{financer.account-new.label.type}"/> + <select id="selectType" th:field="*{type}"> + <option th:each="type : ${accounttypes}" th:value="${type}" th:text="#{'financer.account-type.' + ${type}}"/> + </select> + <input type="submit" th:value="#{financer.account-new.submit}" /> +</form> +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/recurringTransaction/newRecurringTransaction.html b/src/main/resources/templates/recurringTransaction/newRecurringTransaction.html new file mode 100644 index 0000000..6ddd215 --- /dev/null +++ b/src/main/resources/templates/recurringTransaction/newRecurringTransaction.html @@ -0,0 +1,45 @@ +<!DOCTYPE HTML> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <title th:text="#{financer.recurring-transaction-new.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}"> +</head> +<body> +<h1 th:text="#{financer.heading.recurring-transaction-new}" /> +<span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/> +<form id="new-recurring-transaction-form" action="#" th:action="@{/saveRecurringTransaction}" th:object="${newRecurringTransactionForm}" + 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}" + th:text="#{'financer.recurring-transaction-new.account-type.' + ${acc.type}(${acc.key},${#numbers.formatDecimal(acc.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT')})}"/> + </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}" + th:text="#{'financer.recurring-transaction-new.account-type.' + ${acc.type}(${acc.key},${#numbers.formatDecimal(acc.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT')})}"/> + </select> + <label for="inputAmount" th:text="#{financer.recurring-transaction-new.label.amount}"/> + <input type="text" id="inputAmount" th:field="*{amount}"/> + <label for="inputFirstOccurrence" th:text="#{financer.recurring-transaction-new.label.first-occurrence}"/> + <input type="date" id="inputFirstOccurrence" th:field="*{firstOccurrence}"/> + <label for="inputLastOccurrence" th:text="#{financer.recurring-transaction-new.label.last-occurrence}"/> + <input type="date" id="inputLastOccurrence" th:field="*{lastOccurrence}"/> + <label for="selectInterval" th:text="#{financer.recurring-transaction-new.label.interval-type}"/> + <select id="selectInterval" th:field="*{intervalType}"> + <option th:each="inter : ${intervalTypes}" th:value="${inter}" + th:text="#{'financer.interval-type.' + ${inter}}"/> + </select> + <label for="selectHolidayWeekend" th:text="#{financer.recurring-transaction-new.label.holiday-weekend-type}"/> + <select id="selectHolidayWeekend" th:field="*{holidayWeekendType}"> + <option th:each="hdwt : ${holidayWeekendTypes}" th:value="${hdwt}" + th:text="#{'financer.holiday-weekend-type.' + ${hdwt}}"/> + </select> + <label for="inputDescription" th:text="#{financer.recurring-transaction-new.label.description}"/> + <input type="text" id="inputDescription" th:field="*{description}"/> + <input type="submit" th:value="#{financer.recurring-transaction-new.submit}"/> +</form> +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/recurringTransaction/recurringTransactionList.html b/src/main/resources/templates/recurringTransaction/recurringTransactionList.html new file mode 100644 index 0000000..e77f81b --- /dev/null +++ b/src/main/resources/templates/recurringTransaction/recurringTransactionList.html @@ -0,0 +1,53 @@ +<!DOCTYPE HTML> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <title th:text="#{'financer.recurring-transaction-list.title.' + ${subTitle}}"/> + <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}"> +</head> +<body> +<h1 th:text="#{'financer.heading.recurring-transaction-list.' + ${subTitle}}"/> +<table id="recurring-transaction-list-table"> + <tr> + <th class="hideable-column" th:text="#{financer.recurring-transaction-list.table-header.id}"/> + <th th:text="#{financer.recurring-transaction-list.table-header.fromAccount}"/> + <th th:text="#{financer.recurring-transaction-list.table-header.toAccount}"/> + <th th:text="#{financer.recurring-transaction-list.table-header.firstOccurrence}"/> + <th th:text="#{financer.recurring-transaction-list.table-header.lastOccurrence}"/> + <th th:text="#{financer.recurring-transaction-list.table-header.amount}"/> + <th th:text="#{financer.recurring-transaction-list.table-header.description}"/> + <th th:text="#{financer.recurring-transaction-list.table-header.intervalType}"/> + <th th:text="#{financer.recurring-transaction-list.table-header.holidayWeekendType}"/> + <th th:text="#{financer.recurring-transaction-list.table-header.actions}"/> + </tr> + <tr th:each="rt : ${recurringTransactions}"> + <td class="hideable-column" th:text="${rt.id}"/> + <td> + <a th:href="@{/accountDetails(key=${rt.fromAccount.key})}" th:text="${rt.fromAccount.key}"/> + </td> + <td> + <a th:href="@{/accountDetails(key=${rt.toAccount.key})}" th:text="${rt.toAccount.key}"/> + </td> + <td th:text="${rt.firstOccurrence}"/> + <td th:text="${rt.lastOccurrence}"/> + <td th:text="${#numbers.formatDecimal(rt.amount/100D, 1, 'DEFAULT', 2, 'DEFAULT')}"/> + <td th:text="${rt.description}"/> + <td th:text="${rt.intervalType}"/> + <td th:text="${rt.holidayWeekendType}"/> + <td> + <div id="recurring-transaction-list-table-actions-container"> + <a th:href="@{/recurringToTransaction}" + th:text="#{financer.recurring-transaction-list.table.actions.createTransaction}"/> + <a th:href="@{/recurringToTransactionWithAmount}" + th:text="#{financer.recurring-transaction-list.table.actions.createTransactionWithAmount}"/> + <a th:href="@{/editRecurringTransaction}" + th:text="#{financer.recurring-transaction-list.table.actions.editRecurringTransaction}"/> + <a th:href="@{/deleteRecurringTransaction}" + th:text="#{financer.recurring-transaction-list.table.actions.deleteRecurringTransaction}"/> + </div> + </td> + </tr> +</table> +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/transaction/newTransaction.html b/src/main/resources/templates/transaction/newTransaction.html new file mode 100644 index 0000000..5e6d29f --- /dev/null +++ b/src/main/resources/templates/transaction/newTransaction.html @@ -0,0 +1,33 @@ +<!DOCTYPE HTML> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <title th:text="#{financer.transaction-new.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}"> +</head> +<body> +<h1 th:text="#{financer.heading.transaction-new}" /> +<span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/> +<form id="new-transaction-form" action="#" th:action="@{/saveTransaction}" th:object="${newTransactionForm}" + 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}" + th:text="#{'financer.transaction-new.account-type.' + ${acc.type}(${acc.key},${#numbers.formatDecimal(acc.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT')})}"/> + </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}" + th:text="#{'financer.transaction-new.account-type.' + ${acc.type}(${acc.key},${#numbers.formatDecimal(acc.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT')})}"/> + </select> + <label for="inputAmount" th:text="#{financer.transaction-new.label.amount}"/> + <input type="text" id="inputAmount" th:field="*{amount}"/> + <label for="inputDate" th:text="#{financer.transaction-new.label.date}"/> + <input type="date" id="inputDate" th:field="*{date}"/> + <label for="inputDescription" th:text="#{financer.transaction-new.label.description}"/> + <input type="text" id="inputDescription" th:field="*{description}"/> + <input type="submit" th:value="#{financer.transaction-new.submit}"/> +</form> +</body> +</html> \ No newline at end of file diff --git a/src/test/java/de/financer/FinancerApplicationBootTest.java b/src/test/java/de/financer/FinancerApplicationBootTest.java new file mode 100644 index 0000000..21ec49f --- /dev/null +++ b/src/test/java/de/financer/FinancerApplicationBootTest.java @@ -0,0 +1,31 @@ +package de.financer; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = FinancerApplication.class) +@AutoConfigureMockMvc +@TestPropertySource( + locations = "classpath:application-integrationtest.properties") +public class FinancerApplicationBootTest { + @Autowired + private MockMvc mockMvc; + + @Test + public void test_appBoots() { + // Nothing to do in this test as we just want to startup the app + // to make sure that spring, flyway and hibernate all work + // as expected even after changes + // While this slightly increases build time it's an easy and safe + // way to ensure that the app can start + Assert.assertTrue(true); + } +} diff --git a/src/test/resources/application-integrationtest.properties b/src/test/resources/application-integrationtest.properties new file mode 100644 index 0000000..1fcad4a --- /dev/null +++ b/src/test/resources/application-integrationtest.properties @@ -0,0 +1,5 @@ +spring.profiles.active=dev + +spring.datasource.url=jdbc:hsqldb:mem:. +spring.datasource.username=sa +spring.flyway.locations=classpath:/database/hsqldb,classpath:/database/hsqldb/integration,classpath:/database/common From 12a8b805fa2ad564f17ddeb3024c32e724bd3804 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 5 May 2019 02:03:02 +0200 Subject: [PATCH 02/32] Stuff all over the place --- .../de/financer/config/FinancerConfig.java | 9 + .../controller/AccountController.java | 41 ++++- .../RecurringTransactionController.java | 162 +++++++++++++++--- .../controller/TransactionController.java | 60 ++++++- .../RecurringToTransactionWithAmountForm.java | 31 ++++ .../de/financer/util/ControllerUtils.java | 5 + .../de/financer/util/TransactionUtils.java | 64 +++++++ .../TransactionByDateComparator.java | 12 ++ .../resources/config/application.properties | 2 +- src/main/resources/i18n/message.properties | 7 +- .../resources/i18n/message_de_DE.properties | 11 +- src/main/resources/static/css/main.css | 19 +- .../templates/account/accountDetails.html | 7 +- .../templates/account/accountOverview.html | 1 + .../templates/account/newAccount.html | 3 +- .../resources/templates/includes/footer.html | 4 + .../newRecurringTransaction.html | 3 +- .../recurringToTransactionWithAmount.html | 22 +++ .../recurringTransactionList.html | 14 +- .../templates/transaction/newTransaction.html | 2 +- 20 files changed, 422 insertions(+), 57 deletions(-) create mode 100644 src/main/java/de/financer/form/RecurringToTransactionWithAmountForm.java create mode 100644 src/main/java/de/financer/util/TransactionUtils.java create mode 100644 src/main/java/de/financer/util/comparator/TransactionByDateComparator.java create mode 100644 src/main/resources/templates/includes/footer.html create mode 100644 src/main/resources/templates/recurringTransaction/recurringToTransactionWithAmount.html diff --git a/src/main/java/de/financer/config/FinancerConfig.java b/src/main/java/de/financer/config/FinancerConfig.java index 91a368d..26438a0 100644 --- a/src/main/java/de/financer/config/FinancerConfig.java +++ b/src/main/java/de/financer/config/FinancerConfig.java @@ -12,6 +12,7 @@ public class FinancerConfig { private String serverUrl; private String dateFormat; + private String version; public String getServerUrl() { return serverUrl; @@ -28,4 +29,12 @@ public class FinancerConfig { public void setDateFormat(String dateFormat) { this.dateFormat = dateFormat; } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } } diff --git a/src/main/java/de/financer/controller/AccountController.java b/src/main/java/de/financer/controller/AccountController.java index 9fa2b36..c17a11f 100644 --- a/src/main/java/de/financer/controller/AccountController.java +++ b/src/main/java/de/financer/controller/AccountController.java @@ -6,6 +6,8 @@ import de.financer.controller.template.*; import de.financer.form.NewAccountForm; import de.financer.model.*; import de.financer.util.ControllerUtils; +import de.financer.util.TransactionUtils; +import de.financer.util.comparator.TransactionByDateComparator; import org.apache.commons.collections4.IterableUtils; import org.apache.commons.lang3.BooleanUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -16,6 +18,8 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.util.UriComponentsBuilder; +import java.util.List; + @Controller public class AccountController { @Autowired @@ -34,6 +38,7 @@ public class AccountController { model.addAttribute("rtDueTodayCount", IterableUtils.size(rtDtRes.getBody())); model.addAttribute("rtAllActiveCount", IterableUtils.size(rtAllActRes.getBody())); model.addAttribute("showClosed", showClosedBoolean); + ControllerUtils.addVersionAttribute(model, this.financerConfig); return "account/accountOverview"; } @@ -41,25 +46,27 @@ public class AccountController { @GetMapping("/newAccount") public String newAccount(Model model) { model.addAttribute("accounttypes", AccountType.valueList()); - model.addAttribute("newAccountForm", new NewAccountForm()); + model.addAttribute("form", new NewAccountForm()); + ControllerUtils.addVersionAttribute(model, this.financerConfig); return "account/newAccount"; } @PostMapping("/saveAccount") - public String saveAccont(NewAccountForm newAccountForm, Model model) { + public String saveAccont(NewAccountForm form, Model model) { final UriComponentsBuilder builder = UriComponentsBuilder .fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.ACC_CREATE_ACCOUNT)) - .queryParam("key", newAccountForm.getKey()) - .queryParam("type", newAccountForm.getType()); + .queryParam("key", form.getKey()) + .queryParam("type", form.getType()); final ResponseEntity<String> response = new StringTemplate().exchange(builder); final ResponseReason responseReason = ResponseReason.fromResponseEntity(response); if (!ResponseReason.OK.equals(responseReason)) { - model.addAttribute("form", newAccountForm); + model.addAttribute("form", form); model.addAttribute("accounttypes", AccountType.valueList()); model.addAttribute("errorMessage", responseReason.name()); + ControllerUtils.addVersionAttribute(model, this.financerConfig); return "account/newAccount"; } @@ -74,9 +81,15 @@ public class AccountController { final ResponseEntity<Iterable<Transaction>> transactionResponse = new GetAllTransactionsForAccountTemplate() .exchange(this.financerConfig, account.getKey()); + List<Transaction> transactions = IterableUtils.toList(transactionResponse.getBody()); + + transactions.sort(new TransactionByDateComparator()); + transactions.stream().forEach((t) -> TransactionUtils.adjustAmount(t, account)); + model.addAttribute("account", account); - model.addAttribute("transactions", IterableUtils.toList(transactionResponse.getBody())); + model.addAttribute("transactions", transactions); model.addAttribute("isClosed", AccountStatus.CLOSED.equals(account.getStatus())); + ControllerUtils.addVersionAttribute(model, this.financerConfig); return "account/accountDetails"; } @@ -97,10 +110,16 @@ public class AccountController { final ResponseEntity<Iterable<Transaction>> transactionResponse = new GetAllTransactionsForAccountTemplate() .exchange(this.financerConfig, account.getKey()); + List<Transaction> transactions = IterableUtils.toList(transactionResponse.getBody()); + + transactions.sort(new TransactionByDateComparator()); + transactions.stream().forEach((t) -> TransactionUtils.adjustAmount(t, account)); + model.addAttribute("account", account); - model.addAttribute("transactions", IterableUtils.toList(transactionResponse.getBody())); + model.addAttribute("transactions", transactions); model.addAttribute("isClosed", AccountStatus.CLOSED.equals(account.getStatus())); model.addAttribute("errorMessage", responseReason.name()); + ControllerUtils.addVersionAttribute(model, this.financerConfig); return "account/accountDetails"; } @@ -124,10 +143,16 @@ public class AccountController { final ResponseEntity<Iterable<Transaction>> transactionResponse = new GetAllTransactionsForAccountTemplate() .exchange(this.financerConfig, account.getKey()); + List<Transaction> transactions = IterableUtils.toList(transactionResponse.getBody()); + + transactions.sort(new TransactionByDateComparator()); + transactions.stream().forEach((t) -> TransactionUtils.adjustAmount(t, account)); + model.addAttribute("account", account); - model.addAttribute("transactions", IterableUtils.toList(transactionResponse.getBody())); + model.addAttribute("transactions", transactions); model.addAttribute("isClosed", AccountStatus.CLOSED.equals(account.getStatus())); model.addAttribute("errorMessage", responseReason.name()); + ControllerUtils.addVersionAttribute(model, this.financerConfig); return "account/accountDetails"; } diff --git a/src/main/java/de/financer/controller/RecurringTransactionController.java b/src/main/java/de/financer/controller/RecurringTransactionController.java index 397efc1..68417ba 100644 --- a/src/main/java/de/financer/controller/RecurringTransactionController.java +++ b/src/main/java/de/financer/controller/RecurringTransactionController.java @@ -4,6 +4,7 @@ import de.financer.ResponseReason; import de.financer.config.FinancerConfig; import de.financer.controller.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; @@ -17,6 +18,8 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.util.UriComponentsBuilder; +import java.util.ArrayList; + @Controller public class RecurringTransactionController { @@ -30,33 +33,24 @@ public class RecurringTransactionController { model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(response.getBody())); model.addAttribute("intervalTypes", IntervalType.valueList()); model.addAttribute("holidayWeekendTypes", HolidayWeekendType.valueList()); - model.addAttribute("newRecurringTransactionForm", new NewRecurringTransactionForm()); + model.addAttribute("form", new NewRecurringTransactionForm()); + ControllerUtils.addVersionAttribute(model, this.financerConfig); return "recurringTransaction/newRecurringTransaction"; } @PostMapping("/saveRecurringTransaction") - public String saveRecurringTransaction(NewRecurringTransactionForm newRecurringTransactionForm, Model model) { + public String saveRecurringTransaction(NewRecurringTransactionForm form, Model model) { final UriComponentsBuilder builder = UriComponentsBuilder .fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.RT_CREATE_RECURRING_TRANSACTION)) - .queryParam("fromAccountKey", newRecurringTransactionForm - .getFromAccountKey()) - .queryParam("toAccountKey", newRecurringTransactionForm - .getToAccountKey()) - .queryParam("amount", newRecurringTransactionForm - .getAmount()) - .queryParam("firstOccurrence", ControllerUtils - .formatDate(this.financerConfig, newRecurringTransactionForm - .getFirstOccurrence())) - .queryParam("lastOccurrence", ControllerUtils - .formatDate(this.financerConfig, newRecurringTransactionForm - .getLastOccurrence())) - .queryParam("holidayWeekendType", newRecurringTransactionForm - .getHolidayWeekendType()) - .queryParam("intervalType", newRecurringTransactionForm - .getIntervalType()) - .queryParam("description", newRecurringTransactionForm - .getDescription()); + .queryParam("fromAccountKey", form.getFromAccountKey()) + .queryParam("toAccountKey", form.getToAccountKey()) + .queryParam("amount", form.getAmount()) + .queryParam("firstOccurrence", ControllerUtils.formatDate(this.financerConfig, form.getFirstOccurrence())) + .queryParam("lastOccurrence", ControllerUtils.formatDate(this.financerConfig, form.getLastOccurrence())) + .queryParam("holidayWeekendType", form.getHolidayWeekendType()) + .queryParam("intervalType", form.getIntervalType()) + .queryParam("description", form.getDescription()); final ResponseEntity<String> response = new StringTemplate().exchange(builder); @@ -69,8 +63,9 @@ public class RecurringTransactionController { model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(getAllResponse.getBody())); model.addAttribute("intervalTypes", IntervalType.valueList()); model.addAttribute("holidayWeekendTypes", HolidayWeekendType.valueList()); - model.addAttribute("form", newRecurringTransactionForm); + model.addAttribute("form", form); model.addAttribute("errorMessage", responseReason.name()); + ControllerUtils.addVersionAttribute(model, this.financerConfig); return "recurringTransaction/newRecurringTransaction"; } @@ -85,6 +80,7 @@ public class RecurringTransactionController { model.addAttribute("recurringTransactions", response.getBody()); model.addAttribute("subTitle", "dueToday"); + ControllerUtils.addVersionAttribute(model, this.financerConfig); return "recurringTransaction/recurringTransactionList"; } @@ -96,6 +92,7 @@ public class RecurringTransactionController { model.addAttribute("recurringTransactions", response.getBody()); model.addAttribute("subTitle", "active"); + ControllerUtils.addVersionAttribute(model, this.financerConfig); return "recurringTransaction/recurringTransactionList"; } @@ -107,7 +104,130 @@ public class RecurringTransactionController { model.addAttribute("recurringTransactions", response.getBody()); model.addAttribute("subTitle", "all"); + ControllerUtils.addVersionAttribute(model, this.financerConfig); return "recurringTransaction/recurringTransactionList"; } + + @GetMapping("/deleteRecurringTransaction") + public String deleteRecurringTransaction(String recurringTransactionId, Model model) { + final UriComponentsBuilder builder = UriComponentsBuilder + .fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.RT_DELETE_RECURRING_TRANSACTION)) + .queryParam("recurringTransactionId", recurringTransactionId); + + final ResponseEntity<String> response = new StringTemplate().exchange(builder); + final ResponseReason responseReason = ResponseReason.fromResponseEntity(response); + final ResponseEntity<Iterable<RecurringTransaction>> allResponse = new GetAllRecurringTransactionsTemplate() + .exchange(this.financerConfig); + + model.addAttribute("recurringTransactions", allResponse.getBody()); + model.addAttribute("subTitle", "all"); + ControllerUtils.addVersionAttribute(model, this.financerConfig); + + if (!ResponseReason.OK.equals(responseReason)) { + model.addAttribute("errorMessage", responseReason.name()); + + return "recurringTransaction/recurringTransactionList"; + } + + return "recurringTransaction/recurringTransactionList"; + } + + @GetMapping("/recurringToTransaction") + public String recurringToTransaction(String recurringTransactionId, String sub, Model model) { + final UriComponentsBuilder builder = UriComponentsBuilder + .fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.RT_CREATE_TRANSACTION)) + .queryParam("recurringTransactionId", recurringTransactionId); + + final ResponseEntity<String> response = new StringTemplate().exchange(builder); + final ResponseReason responseReason = ResponseReason.fromResponseEntity(response); + + if (!ResponseReason.OK.equals(responseReason)) { + model.addAttribute("errorMessage", responseReason.name()); + model.addAttribute("subTitle", sub); + ControllerUtils.addVersionAttribute(model, this.financerConfig); + + Iterable<RecurringTransaction> recurringTransactions; + + switch (sub) { + case "all": + recurringTransactions = new GetAllRecurringTransactionsTemplate() + .exchange(this.financerConfig).getBody(); + break; + case "active": + recurringTransactions = new GetAllRecurringTransactionsTemplate() + .exchange(this.financerConfig).getBody(); + break; + case "dueToday": + recurringTransactions = new GetAllRecurringTransactionsDueTodayTemplate() + .exchange(this.financerConfig).getBody(); + break; + default: + recurringTransactions = new ArrayList<>(); + } + + model.addAttribute("recurringTransactions", recurringTransactions); + + return "recurringTransaction/recurringTransactionList"; + } + + return "redirect:/accountOverview"; + } + + @GetMapping("/recurringToTransactionWithAmount") + public String recurringToTransactionWithAmount(String recurringTransactionId, String sub, Model model) { + ControllerUtils.addVersionAttribute(model, this.financerConfig); + + RecurringToTransactionWithAmountForm form = new RecurringToTransactionWithAmountForm(); + + form.setRecurringTransactionId(recurringTransactionId); + form.setSubTitle(sub); + + model.addAttribute("form", form); + + return "recurringTransaction/recurringToTransactionWithAmount"; + } + + @PostMapping("/createTransactionWithAmount") + public String createTransactionWithAmount(RecurringToTransactionWithAmountForm form, Model model) { + final UriComponentsBuilder builder = UriComponentsBuilder + .fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.RT_CREATE_TRANSACTION)) + .queryParam("recurringTransactionId", form.getRecurringTransactionId()) + .queryParam("amount", form.getAmount()); + + final ResponseEntity<String> response = new StringTemplate().exchange(builder); + final ResponseReason responseReason = ResponseReason.fromResponseEntity(response); + + if (!ResponseReason.OK.equals(responseReason)) { + model.addAttribute("errorMessage", responseReason.name()); + model.addAttribute("subTitle", form.getSubTitle()); + ControllerUtils.addVersionAttribute(model, this.financerConfig); + + Iterable<RecurringTransaction> recurringTransactions; + + switch (form.getSubTitle()) { + case "all": + recurringTransactions = new GetAllRecurringTransactionsTemplate() + .exchange(this.financerConfig).getBody(); + break; + case "active": + recurringTransactions = new GetAllRecurringTransactionsTemplate() + .exchange(this.financerConfig).getBody(); + break; + case "dueToday": + recurringTransactions = new GetAllRecurringTransactionsDueTodayTemplate() + .exchange(this.financerConfig).getBody(); + break; + default: + recurringTransactions = new ArrayList<>(); + } + + model.addAttribute("recurringTransactions", recurringTransactions); + + return "recurringTransaction/recurringTransactionList"; + } + + return "redirect:/accountOverview"; + } + } diff --git a/src/main/java/de/financer/controller/TransactionController.java b/src/main/java/de/financer/controller/TransactionController.java index 6eeec4c..edce268 100644 --- a/src/main/java/de/financer/controller/TransactionController.java +++ b/src/main/java/de/financer/controller/TransactionController.java @@ -2,11 +2,18 @@ package de.financer.controller; import de.financer.ResponseReason; import de.financer.config.FinancerConfig; +import de.financer.controller.template.GetAccountByKeyTemplate; import de.financer.controller.template.GetAllAccountsTemplate; +import de.financer.controller.template.GetAllTransactionsForAccountTemplate; import de.financer.controller.template.StringTemplate; import de.financer.form.NewTransactionForm; import de.financer.model.Account; +import de.financer.model.AccountStatus; +import de.financer.model.Transaction; import de.financer.util.ControllerUtils; +import de.financer.util.TransactionUtils; +import de.financer.util.comparator.TransactionByDateComparator; +import org.apache.commons.collections4.IterableUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -15,6 +22,8 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.util.UriComponentsBuilder; +import java.util.List; + @Controller public class TransactionController { @@ -26,20 +35,21 @@ public class TransactionController { final ResponseEntity<Iterable<Account>> response = new GetAllAccountsTemplate().exchange(this.financerConfig); model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(response.getBody())); - model.addAttribute("newTransactionForm", new NewTransactionForm()); + model.addAttribute("form", new NewTransactionForm()); + ControllerUtils.addVersionAttribute(model, this.financerConfig); return "transaction/newTransaction"; } @PostMapping("/saveTransaction") - public String saveTransaction(NewTransactionForm newTransactionForm, Model model) { + public String saveTransaction(NewTransactionForm form, Model model) { final UriComponentsBuilder builder = UriComponentsBuilder .fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.TR_CREATE_TRANSACTION)) - .queryParam("fromAccountKey", newTransactionForm.getFromAccountKey()) - .queryParam("toAccountKey", newTransactionForm.getToAccountKey()) - .queryParam("amount", newTransactionForm.getAmount()) - .queryParam("date", ControllerUtils.formatDate(this.financerConfig, newTransactionForm.getDate())) - .queryParam("description", newTransactionForm.getDescription()); + .queryParam("fromAccountKey", form.getFromAccountKey()) + .queryParam("toAccountKey", form.getToAccountKey()) + .queryParam("amount", form.getAmount()) + .queryParam("date", ControllerUtils.formatDate(this.financerConfig, form.getDate())) + .queryParam("description", form.getDescription()); final ResponseEntity<String> response = new StringTemplate().exchange(builder); final ResponseReason responseReason = ResponseReason.fromResponseEntity(response); @@ -48,12 +58,46 @@ public class TransactionController { final ResponseEntity<Iterable<Account>> accRes = new GetAllAccountsTemplate().exchange(this.financerConfig); model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(accRes.getBody())); - model.addAttribute("form", newTransactionForm); + model.addAttribute("form", form); model.addAttribute("errorMessage", responseReason.name()); + ControllerUtils.addVersionAttribute(model, this.financerConfig); return "transaction/newTransaction"; } return "redirect:/accountOverview"; } + + @GetMapping("/deleteTransaction") + public String deleteTransaction(String transactionId, String accountKey, Model model) { + final UriComponentsBuilder builder = UriComponentsBuilder + .fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.TR_DELETE_TRANSACTION)) + .queryParam("transactionId", transactionId); + + 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 Account account = accountResponse.getBody(); + final ResponseEntity<Iterable<Transaction>> transactionResponse = new GetAllTransactionsForAccountTemplate() + .exchange(this.financerConfig, account.getKey()); + + List<Transaction> transactions = IterableUtils.toList(transactionResponse.getBody()); + + transactions.sort(new TransactionByDateComparator()); + transactions.stream().forEach((t) -> TransactionUtils.adjustAmount(t, account)); + + model.addAttribute("account", account); + model.addAttribute("transactions", transactions); + model.addAttribute("isClosed", AccountStatus.CLOSED.equals(account.getStatus())); + ControllerUtils.addVersionAttribute(model, this.financerConfig); + + if (!ResponseReason.OK.equals(responseReason)) { + model.addAttribute("errorMessage", responseReason.name()); + + return "account/accountDetails"; + } + + return "account/accountDetails"; + } } diff --git a/src/main/java/de/financer/form/RecurringToTransactionWithAmountForm.java b/src/main/java/de/financer/form/RecurringToTransactionWithAmountForm.java new file mode 100644 index 0000000..44c3e79 --- /dev/null +++ b/src/main/java/de/financer/form/RecurringToTransactionWithAmountForm.java @@ -0,0 +1,31 @@ +package de.financer.form; + +public class RecurringToTransactionWithAmountForm { + private String recurringTransactionId; + private String amount; + private String subTitle; + + public String getRecurringTransactionId() { + return recurringTransactionId; + } + + public void setRecurringTransactionId(String recurringTransactionId) { + this.recurringTransactionId = recurringTransactionId; + } + + public String getAmount() { + return amount; + } + + public void setAmount(String amount) { + this.amount = amount; + } + + public String getSubTitle() { + return subTitle; + } + + public void setSubTitle(String subTitle) { + this.subTitle = subTitle; + } +} diff --git a/src/main/java/de/financer/util/ControllerUtils.java b/src/main/java/de/financer/util/ControllerUtils.java index b9debf2..64ce300 100644 --- a/src/main/java/de/financer/util/ControllerUtils.java +++ b/src/main/java/de/financer/util/ControllerUtils.java @@ -8,6 +8,7 @@ import de.financer.model.RecurringTransaction; import de.financer.util.comparator.AccountByTypeComparator; import org.apache.commons.collections4.IterableUtils; import org.apache.commons.lang3.StringUtils; +import org.springframework.ui.Model; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -47,4 +48,8 @@ public class ControllerUtils { .filter((rt) -> rt.getFromAccount() != null && rt.getToAccount() != null) .collect(Collectors.toList()); } + + public static void addVersionAttribute(Model model, FinancerConfig financerConfig) { + model.addAttribute("financerVersion", financerConfig.getVersion()); + } } diff --git a/src/main/java/de/financer/util/TransactionUtils.java b/src/main/java/de/financer/util/TransactionUtils.java new file mode 100644 index 0000000..444d3d0 --- /dev/null +++ b/src/main/java/de/financer/util/TransactionUtils.java @@ -0,0 +1,64 @@ +package de.financer.util; + +import de.financer.model.Account; +import de.financer.model.AccountType; +import de.financer.model.Transaction; + +import static de.financer.model.AccountType.*; + +public class TransactionUtils { + public static final void adjustAmount(Transaction t, Account account) { + boolean isFrom = t.getFromAccount().getKey().equals(account.getKey()); + + if (AccountType.START.equals(t.getFromAccount().getType()) && AccountType.LIABILITY.equals(t.getToAccount().getType())) { + return; + } + + if (isFrom) { + t.setAmount(t.getAmount() * TransactionUtils.getMultiplierFromAccount(account)); + } + else { + t.setAmount(t.getAmount() * TransactionUtils.getMultiplierToAccount(account)); + } + } + + public static long getMultiplierFromAccount(Account fromAccount) { + // There is no multiplier if the from account is an EXPENSE account because + // it's not a valid from account type + + final AccountType accountType = fromAccount.getType(); + + if (INCOME.equals(accountType)) { + return 1L; + } else if (BANK.equals(accountType)) { + return -1L; + } else if (CASH.equals(accountType)) { + return -1L; + } else if (LIABILITY.equals(accountType)) { + return 1L; + } else if (START.equals(accountType)) { + return 1L; + } + + return 1L; + } + + public static long getMultiplierToAccount(Account toAccount) { + // There are no multipliers for INCOME and START accounts + // because they are not valid to account types + + final AccountType accountType = toAccount.getType(); + + if (BANK.equals(accountType)) { + return 1L; + } else if (CASH.equals(accountType)) { + return 1L; + } else if (LIABILITY.equals(accountType)) { + return -1L; + } else if (EXPENSE.equals(accountType)) { + return 1L; + } + + return -1L; + } +} diff --git a/src/main/java/de/financer/util/comparator/TransactionByDateComparator.java b/src/main/java/de/financer/util/comparator/TransactionByDateComparator.java new file mode 100644 index 0000000..c445fc8 --- /dev/null +++ b/src/main/java/de/financer/util/comparator/TransactionByDateComparator.java @@ -0,0 +1,12 @@ +package de.financer.util.comparator; + +import de.financer.model.Transaction; + +import java.util.Comparator; + +public class TransactionByDateComparator implements Comparator<Transaction> { + @Override + public int compare(Transaction o1, Transaction o2) { + return o1.getDate().compareTo(o2.getDate()) * -1; + } +} diff --git a/src/main/resources/config/application.properties b/src/main/resources/config/application.properties index 3da6796..38f476d 100644 --- a/src/main/resources/config/application.properties +++ b/src/main/resources/config/application.properties @@ -19,7 +19,7 @@ logging.file=financer-web-client.log # The date format of the client-supplied date string, used to parse the string into a proper object financer.dateFormat=dd.MM.yyyy - +financer.version=@project.version@ financer.serverUrl=http://localhost:8089/financer-server/ spring.messages.basename=i18n/message \ No newline at end of file diff --git a/src/main/resources/i18n/message.properties b/src/main/resources/i18n/message.properties index b2adb1f..51565ac 100644 --- a/src/main/resources/i18n/message.properties +++ b/src/main/resources/i18n/message.properties @@ -73,6 +73,7 @@ financer.account-details.title=financer\: account details 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.table-header.id=ID financer.account-details.table-header.fromAccount=From account financer.account-details.table-header.toAccount=To account @@ -83,9 +84,12 @@ financer.account-details.table-header.byRecurring=Recurring? financer.account-details.details.type=Type\: financer.account-details.details.balance=Current Balance\: financer.account-details.table-header.actions=Actions -financer.account-details.table.actions.editTransaction=Edit financer.account-details.table.actions.deleteTransaction=Delete +financer.recurring-to-transaction-with-amount.title=financer\: create transaction from recurring with amount +financer.recurring-to-transaction-with-amount.label.amount=Amount\: +financer.recurring-to-transaction-with-amount.submit=Create + financer.interval-type.DAILY=Daily financer.interval-type.WEEKLY=Weekly financer.interval-type.MONTHLY=Monthly @@ -114,6 +118,7 @@ financer.heading.account-details=financer\: account details for {0} financer.heading.recurring-transaction-list.dueToday=financer\: recurring transactions due today financer.heading.recurring-transaction-list.active=financer\: active recurring transactions financer.heading.recurring-transaction-list.all=financer\: all recurring transaction +financer.heading.recurring-to-transaction-with-amount=financer\: create transaction from recurring with amount 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/src/main/resources/i18n/message_de_DE.properties b/src/main/resources/i18n/message_de_DE.properties index 045cf67..7c99954 100644 --- a/src/main/resources/i18n/message_de_DE.properties +++ b/src/main/resources/i18n/message_de_DE.properties @@ -73,6 +73,7 @@ financer.account-details.title=financer\: Kontodetails 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.table-header.id=ID financer.account-details.table-header.fromAccount=Von Konto financer.account-details.table-header.toAccount=An Konto @@ -83,8 +84,11 @@ financer.account-details.table-header.byRecurring=Wiederkehrend? financer.account-details.details.type=Typ\: financer.account-details.details.balance=Kontostand\: financer.account-details.table-header.actions=Aktionen -financer.account-details.table.actions.editTransaction=Bearbeiten -financer.account-details.table.actions.deleteTransaction=Löschen +financer.account-details.table.actions.deleteTransaction=L\u00F6schen + +financer.recurring-to-transaction-with-amount.title=financer\: Buchung mit Betrag aus wiederkehrender Buchung erstellen +financer.recurring-to-transaction-with-amount.label.amount=Betrag\: +financer.recurring-to-transaction-with-amount.submit=Erstellen financer.interval-type.DAILY=T\u00E4glich financer.interval-type.WEEKLY=W\u00F6chentlich @@ -113,4 +117,5 @@ financer.heading.account-overview=financer\: \u00DCbersicht financer.heading.account-details=financer\: Kontodetails f\u00FCr {0} financer.heading.recurring-transaction-list.dueToday=financer\: wiederkehrende Buchungen heute f\u00E4llig financer.heading.recurring-transaction-list.active=financer\: aktive wiederkehrende Buchungen -financer.heading.recurring-transaction-list.all=financer\: alle wiederkehrende Buchungen \ No newline at end of file +financer.heading.recurring-transaction-list.all=financer\: alle wiederkehrende Buchungen +financer.heading.recurring-to-transaction-with-amount=financer\: Buchung mit Betrag aus wiederkehrender Buchung erstellen \ No newline at end of file diff --git a/src/main/resources/static/css/main.css b/src/main/resources/static/css/main.css index eb6b2d9..d3ef84a 100644 --- a/src/main/resources/static/css/main.css +++ b/src/main/resources/static/css/main.css @@ -55,13 +55,28 @@ tr:hover { #new-account-form *, #new-transaction-form *, -#new-recurring-transaction-form * { +#new-recurring-transaction-form *, +#recurring-to-transaction-with-amount-form * { display: block; margin-top: 1em; width: 20em; box-sizing: border-box; } +#footer-container * { + display: block; +} + +#footer-container > hr { + width: 100%; +} + +#footer-container > span { + text-align: center; + font-size: 0.9em; +} + .errorMessage { - color: #ff6666 + color: #ff6666; + display: block; } \ No newline at end of file diff --git a/src/main/resources/templates/account/accountDetails.html b/src/main/resources/templates/account/accountDetails.html index e721517..73cf347 100644 --- a/src/main/resources/templates/account/accountDetails.html +++ b/src/main/resources/templates/account/accountDetails.html @@ -25,6 +25,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="@{/accountOverview}" + th:text="#{financer.account-details.available-actions.back-to-overview}"/> </div> <table id="account-transaction-table"> <tr> @@ -47,13 +49,12 @@ <td th:text="${transaction.recurringTransaction != null}" /> <td> <div id="account-transaction-table-actions-container"> - <a th:href="@{/editTransaction}" - th:text="#{financer.account-details.table.actions.editTransaction}"/> - <a th:href="@{/deleteTransaction}" + <a th:href="@{/deleteTransaction(transactionId=${transaction.id}, accountKey=${account.key})}" th:text="#{financer.account-details.table.actions.deleteTransaction}"/> </div> </td> </tr> </table> +<div th:replace="includes/footer :: footer"/> </body> </html> \ No newline at end of file diff --git a/src/main/resources/templates/account/accountOverview.html b/src/main/resources/templates/account/accountOverview.html index 559e24d..cb63874 100644 --- a/src/main/resources/templates/account/accountOverview.html +++ b/src/main/resources/templates/account/accountOverview.html @@ -50,5 +50,6 @@ <td class="hideable-column" th:text="#{'financer.account-status.' + ${acc.status}}"/> </tr> </table> +<div th:replace="includes/footer :: footer"/> </body> </html> \ No newline at end of file diff --git a/src/main/resources/templates/account/newAccount.html b/src/main/resources/templates/account/newAccount.html index c046626..6534c6c 100644 --- a/src/main/resources/templates/account/newAccount.html +++ b/src/main/resources/templates/account/newAccount.html @@ -9,7 +9,7 @@ <body> <h1 th:text="#{financer.heading.account-new}" /> <span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/> -<form id="new-account-form" action="#" th:action="@{/saveAccount}" th:object="${newAccountForm}" method="post"> +<form id="new-account-form" action="#" th:action="@{/saveAccount}" th:object="${form}" method="post"> <label for="inputKey" th:text="#{financer.account-new.label.key}"/> <input type="text" id="inputKey" th:field="*{key}" placeholder="accounts."/> <label for="selectType" th:text="#{financer.account-new.label.type}"/> @@ -18,5 +18,6 @@ </select> <input type="submit" th:value="#{financer.account-new.submit}" /> </form> +<div th:replace="includes/footer :: footer"/> </body> </html> \ No newline at end of file diff --git a/src/main/resources/templates/includes/footer.html b/src/main/resources/templates/includes/footer.html new file mode 100644 index 0000000..8c9bd8b --- /dev/null +++ b/src/main/resources/templates/includes/footer.html @@ -0,0 +1,4 @@ +<div id="footer-container" th:fragment="footer"> + <hr> + <span th:text="'financer v' + ${financerVersion}"/> +</div> \ No newline at end of file diff --git a/src/main/resources/templates/recurringTransaction/newRecurringTransaction.html b/src/main/resources/templates/recurringTransaction/newRecurringTransaction.html index 6ddd215..9fad278 100644 --- a/src/main/resources/templates/recurringTransaction/newRecurringTransaction.html +++ b/src/main/resources/templates/recurringTransaction/newRecurringTransaction.html @@ -9,7 +9,7 @@ <body> <h1 th:text="#{financer.heading.recurring-transaction-new}" /> <span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/> -<form id="new-recurring-transaction-form" action="#" th:action="@{/saveRecurringTransaction}" th:object="${newRecurringTransactionForm}" +<form id="new-recurring-transaction-form" action="#" th:action="@{/saveRecurringTransaction}" th:object="${form}" method="post"> <label for="selectFromAccount" th:text="#{financer.recurring-transaction-new.label.from-account}"/> <select id="selectFromAccount" th:field="*{fromAccountKey}"> @@ -41,5 +41,6 @@ <input type="text" id="inputDescription" th:field="*{description}"/> <input type="submit" th:value="#{financer.recurring-transaction-new.submit}"/> </form> +<div th:replace="includes/footer :: footer"/> </body> </html> \ No newline at end of file diff --git a/src/main/resources/templates/recurringTransaction/recurringToTransactionWithAmount.html b/src/main/resources/templates/recurringTransaction/recurringToTransactionWithAmount.html new file mode 100644 index 0000000..f96467f --- /dev/null +++ b/src/main/resources/templates/recurringTransaction/recurringToTransactionWithAmount.html @@ -0,0 +1,22 @@ +<!DOCTYPE HTML> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <title th:text="#{financer.recurring-to-transaction-with-amount.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}"> +</head> +<body> +<h1 th:text="#{financer.heading.recurring-to-transaction-with-amount}" /> +<span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/> +<form id="recurring-to-transaction-with-amount-form" action="#" th:action="@{/createTransactionWithAmount}" th:object="${form}" + method="post"> + <label for="inputAmount" th:text="#{financer.recurring-to-transaction-with-amount.label.amount}"/> + <input type="text" id="inputAmount" th:field="*{amount}"/> + <input type="hidden" id="inputId" th:field="*{recurringTransactionId}"/> + <input type="hidden" id="inputSub" th:field="*{subTitle}"/> + <input type="submit" th:value="#{financer.recurring-to-transaction-with-amount.submit}"/> +</form> +<div th:replace="includes/footer :: footer"/> +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/recurringTransaction/recurringTransactionList.html b/src/main/resources/templates/recurringTransaction/recurringTransactionList.html index e77f81b..8a72fba 100644 --- a/src/main/resources/templates/recurringTransaction/recurringTransactionList.html +++ b/src/main/resources/templates/recurringTransaction/recurringTransactionList.html @@ -8,6 +8,7 @@ </head> <body> <h1 th:text="#{'financer.heading.recurring-transaction-list.' + ${subTitle}}"/> +<span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/> <table id="recurring-transaction-list-table"> <tr> <th class="hideable-column" th:text="#{financer.recurring-transaction-list.table-header.id}"/> @@ -33,21 +34,20 @@ <td th:text="${rt.lastOccurrence}"/> <td th:text="${#numbers.formatDecimal(rt.amount/100D, 1, 'DEFAULT', 2, 'DEFAULT')}"/> <td th:text="${rt.description}"/> - <td th:text="${rt.intervalType}"/> - <td th:text="${rt.holidayWeekendType}"/> + <td th:text="#{'financer.interval-type.' + ${rt.intervalType}}"/> + <td th:text="#{'financer.holiday-weekend-type.' + ${rt.holidayWeekendType}}"/> <td> <div id="recurring-transaction-list-table-actions-container"> - <a th:href="@{/recurringToTransaction}" + <a th:href="@{/recurringToTransaction(recurringTransactionId=${rt.id}, sub=${subTitle})}" th:text="#{financer.recurring-transaction-list.table.actions.createTransaction}"/> - <a th:href="@{/recurringToTransactionWithAmount}" + <a th:href="@{/recurringToTransactionWithAmount(recurringTransactionId=${rt.id}, sub=${subTitle})}" th:text="#{financer.recurring-transaction-list.table.actions.createTransactionWithAmount}"/> - <a th:href="@{/editRecurringTransaction}" - th:text="#{financer.recurring-transaction-list.table.actions.editRecurringTransaction}"/> - <a th:href="@{/deleteRecurringTransaction}" + <a th:if="${subTitle == 'all'}" th:href="@{/deleteRecurringTransaction(recurringTransactionId=${rt.id})}" th:text="#{financer.recurring-transaction-list.table.actions.deleteRecurringTransaction}"/> </div> </td> </tr> </table> +<div th:replace="includes/footer :: footer"/> </body> </html> \ No newline at end of file diff --git a/src/main/resources/templates/transaction/newTransaction.html b/src/main/resources/templates/transaction/newTransaction.html index 5e6d29f..791f08f 100644 --- a/src/main/resources/templates/transaction/newTransaction.html +++ b/src/main/resources/templates/transaction/newTransaction.html @@ -9,7 +9,7 @@ <body> <h1 th:text="#{financer.heading.transaction-new}" /> <span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/> -<form id="new-transaction-form" action="#" th:action="@{/saveTransaction}" th:object="${newTransactionForm}" +<form id="new-transaction-form" action="#" th:action="@{/saveTransaction}" th:object="${form}" method="post"> <label for="selectFromAccount" th:text="#{financer.transaction-new.label.from-account}"/> <select id="selectFromAccount" th:field="*{fromAccountKey}"> From ccb9845e8326de278ebdbf442e5d26e042108cb0 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 5 May 2019 02:08:33 +0200 Subject: [PATCH 03/32] financer release version 1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index aab892e..fba7234 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-web-client</artifactId> - <version>1-SNAPSHOT</version> + <version>1</version> <packaging>${packaging.type}</packaging> <description>The web client part of the financer application - a simple app to manage your personal finances</description> <name>financer-web-client</name> From 9c7989d7fff06dae89264caf8815c73acf014a91 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 5 May 2019 02:12:11 +0200 Subject: [PATCH 04/32] Prepare next development iteration for v2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fba7234..be1482d 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-web-client</artifactId> - <version>1</version> + <version>2-SNAPSHOT</version> <packaging>${packaging.type}</packaging> <description>The web client part of the financer application - a simple app to manage your personal finances</description> <name>financer-web-client</name> From 9c404021c4a0a27186f65dc8c3e9277e6f7fef21 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 5 May 2019 11:59:07 +0200 Subject: [PATCH 05/32] Introduce 'fileName' property in POM to enable adjustments of later deployment --- pom.xml | 11 ++++++++++- src/main/resources/config/application-dev.properties | 4 +++- src/main/resources/config/application.properties | 5 ++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index be1482d..99e442a 100644 --- a/pom.xml +++ b/pom.xml @@ -29,6 +29,14 @@ See e.g. https://tomcat.apache.org/tomcat-8.5-doc/config/context.html Should be the same as the project.version but padded with zeros to six digits. --> <parallelDeploymentVersion>000001</parallelDeploymentVersion> + <!-- Property to define the final name of the build artifact. + This used in combination with the build-war profile to change the .war file name + to e.g. financer#00004.war so that the application is accessible via the /financer + context path once deployed in a standalone servlet container instance. This also enables multiple + deployments of the application in the same servlet container just with different context + paths. Default is the artifactId, the build-war profile defines its own default. + Can be changed at build time via 'mvn ... -DfinalName=financer ...--> + <finalName>${project.artifactId}</finalName> </properties> <dependencies> @@ -125,9 +133,10 @@ <properties> <packaging.type>war</packaging.type> <activeProfiles>postgres</activeProfiles> + <finalName>financer</finalName> </properties> <build> - <finalName>${project.artifactId}##${parallelDeploymentVersion}</finalName> + <finalName>${finalName}##${parallelDeploymentVersion}</finalName> </build> <dependencies> <dependency> diff --git a/src/main/resources/config/application-dev.properties b/src/main/resources/config/application-dev.properties index 000f956..7bbdbf2 100644 --- a/src/main/resources/config/application-dev.properties +++ b/src/main/resources/config/application-dev.properties @@ -1,2 +1,4 @@ # Hibernate -spring.jpa.show-sql=true \ No newline at end of file +spring.jpa.show-sql=true + +financer.serverUrl=http://localhost:8089/financer-server/ \ No newline at end of file diff --git a/src/main/resources/config/application.properties b/src/main/resources/config/application.properties index 38f476d..5d2c261 100644 --- a/src/main/resources/config/application.properties +++ b/src/main/resources/config/application.properties @@ -5,6 +5,9 @@ spring.profiles.active=@activeProfiles@ +# This properties are just for the embedded Tomcat used while developing +# In a standalone servlet container instance the context path is usually defined by the name +# of the .war file. See property 'finalName' in the POM for more info. server.servlet.context-path=/financer-web-client server.port=8090 @@ -20,6 +23,6 @@ logging.file=financer-web-client.log # The date format of the client-supplied date string, used to parse the string into a proper object financer.dateFormat=dd.MM.yyyy financer.version=@project.version@ -financer.serverUrl=http://localhost:8089/financer-server/ +financer.serverUrl=http://localhost:8080/financer-server/ spring.messages.basename=i18n/message \ No newline at end of file From 9203bd4ca702a23c5b32829a05efb1660796a9c8 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 5 May 2019 12:00:28 +0200 Subject: [PATCH 06/32] financer release version 2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 99e442a..3375540 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-web-client</artifactId> - <version>2-SNAPSHOT</version> + <version>2</version> <packaging>${packaging.type}</packaging> <description>The web client part of the financer application - a simple app to manage your personal finances</description> <name>financer-web-client</name> From cfdd40652693a476f8b7bfced6b418472508ff73 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 5 May 2019 12:01:43 +0200 Subject: [PATCH 07/32] Prepare next development iteration for v3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3375540..d3e8f93 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-web-client</artifactId> - <version>2</version> + <version>3-SNAPSHOT</version> <packaging>${packaging.type}</packaging> <description>The web client part of the financer application - a simple app to manage your personal finances</description> <name>financer-web-client</name> From 03b09859a237009f39057e8f7392e6ea1e295dd3 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 5 May 2019 12:07:58 +0200 Subject: [PATCH 08/32] Increase parallel deployment version to match the project version --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d3e8f93..d11ac8f 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,7 @@ <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-web-client</artifactId> + <!-- Also increase the parallelDeploymentVersion --> <version>3-SNAPSHOT</version> <packaging>${packaging.type}</packaging> <description>The web client part of the financer application - a simple app to manage your personal finances</description> @@ -28,7 +29,7 @@ <!-- Property to define the parallel deployment version. See e.g. https://tomcat.apache.org/tomcat-8.5-doc/config/context.html Should be the same as the project.version but padded with zeros to six digits. --> - <parallelDeploymentVersion>000001</parallelDeploymentVersion> + <parallelDeploymentVersion>000003</parallelDeploymentVersion> <!-- Property to define the final name of the build artifact. This used in combination with the build-war profile to change the .war file name to e.g. financer#00004.war so that the application is accessible via the /financer From 2e7a809dd42d7997d5f10652ac1f34f4315c43ce Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Thu, 9 May 2019 21:21:15 +0200 Subject: [PATCH 09/32] Map accountOverview to / so it acts as landing page --- src/main/java/de/financer/controller/AccountController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/financer/controller/AccountController.java b/src/main/java/de/financer/controller/AccountController.java index c17a11f..6a39e6e 100644 --- a/src/main/java/de/financer/controller/AccountController.java +++ b/src/main/java/de/financer/controller/AccountController.java @@ -25,7 +25,7 @@ public class AccountController { @Autowired private FinancerConfig financerConfig; - @GetMapping("/accountOverview") + @GetMapping({"/", "/accountOverview"}) public String accountOverview(String showClosed, Model model) { final ResponseEntity<Iterable<Account>> response = new GetAllAccountsTemplate().exchange(this.financerConfig); final ResponseEntity<Iterable<RecurringTransaction>> rtDtRes = new GetAllRecurringTransactionsDueTodayTemplate() From 9282c641d1fc41467e7c0fd65f33c597a4bdeb68 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 12 May 2019 00:43:48 +0200 Subject: [PATCH 10/32] Remove artificial restriction that account keys need to start with 'accounts.' --- src/main/resources/i18n/message.properties | 1 - src/main/resources/templates/account/newAccount.html | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/resources/i18n/message.properties b/src/main/resources/i18n/message.properties index 51565ac..b39ad02 100644 --- a/src/main/resources/i18n/message.properties +++ b/src/main/resources/i18n/message.properties @@ -122,7 +122,6 @@ financer.heading.recurring-to-transaction-with-amount=financer\: create transact 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.INVALID_ACCOUNT_KEY=The entered account key is invalid! It has to start with 'accounts.'. financer.error-message.FROM_ACCOUNT_NOT_FOUND=The specified from account has not been found! financer.error-message.TO_ACCOUNT_NOT_FOUND=The specified to account has not been found! financer.error-message.FROM_AND_TO_ACCOUNT_NOT_FOUND=Neither from nor to have not been found! diff --git a/src/main/resources/templates/account/newAccount.html b/src/main/resources/templates/account/newAccount.html index 6534c6c..e26e3c4 100644 --- a/src/main/resources/templates/account/newAccount.html +++ b/src/main/resources/templates/account/newAccount.html @@ -11,7 +11,7 @@ <span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/> <form id="new-account-form" action="#" th:action="@{/saveAccount}" th:object="${form}" method="post"> <label for="inputKey" th:text="#{financer.account-new.label.key}"/> - <input type="text" id="inputKey" th:field="*{key}" placeholder="accounts."/> + <input type="text" id="inputKey" th:field="*{key}" /> <label for="selectType" th:text="#{financer.account-new.label.type}"/> <select id="selectType" th:field="*{type}"> <option th:each="type : ${accounttypes}" th:value="${type}" th:text="#{'financer.account-type.' + ${type}}"/> From bcca233aac485a448baf5d0b8923dcacbbd6dba9 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 12 May 2019 01:05:37 +0200 Subject: [PATCH 11/32] Adjust log file settings Retain max seven days of logs and change max file size to 50MB --- src/main/resources/config/application.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/config/application.properties b/src/main/resources/config/application.properties index 5d2c261..6db2d9f 100644 --- a/src/main/resources/config/application.properties +++ b/src/main/resources/config/application.properties @@ -19,6 +19,8 @@ info.build.version=@project.version@ logging.level.de.financer=DEBUG logging.file=financer-web-client.log +logging.file.max-history=7 +logging.file.max-size=50MB # The date format of the client-supplied date string, used to parse the string into a proper object financer.dateFormat=dd.MM.yyyy From b68f1a58d8862c879a375e23e9a9c01a256a2761 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 12 May 2019 01:06:49 +0200 Subject: [PATCH 12/32] financer release version 3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d11ac8f..7b09581 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-web-client</artifactId> <!-- Also increase the parallelDeploymentVersion --> - <version>3-SNAPSHOT</version> + <version>3</version> <packaging>${packaging.type}</packaging> <description>The web client part of the financer application - a simple app to manage your personal finances</description> <name>financer-web-client</name> From 1050e9b258efa75a2f2ec896083803fb251f5403 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 12 May 2019 01:07:51 +0200 Subject: [PATCH 13/32] Prepare next development iteration for v4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7b09581..fecb18a 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-web-client</artifactId> <!-- Also increase the parallelDeploymentVersion --> - <version>3</version> + <version>4-SNAPSHOT</version> <packaging>${packaging.type}</packaging> <description>The web client part of the financer application - a simple app to manage your personal finances</description> <name>financer-web-client</name> @@ -29,7 +29,7 @@ <!-- Property to define the parallel deployment version. See e.g. https://tomcat.apache.org/tomcat-8.5-doc/config/context.html Should be the same as the project.version but padded with zeros to six digits. --> - <parallelDeploymentVersion>000003</parallelDeploymentVersion> + <parallelDeploymentVersion>000004</parallelDeploymentVersion> <!-- Property to define the final name of the build artifact. This used in combination with the build-war profile to change the .war file name to e.g. financer#00004.war so that the application is accessible via the /financer From 91d5385dbd54c5f4fc538df1e0581e094ac3db8e Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 12 May 2019 11:13:22 +0200 Subject: [PATCH 14/32] Handle duplicate account key issue gracefully Change transaction propagation to SUPPORTS for that as otherwise an UnexpectedRollbackException gets thrown --- src/main/java/de/financer/ResponseReason.java | 3 ++- src/main/resources/i18n/message.properties | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/financer/ResponseReason.java b/src/main/java/de/financer/ResponseReason.java index 88532a1..01a1025 100644 --- a/src/main/java/de/financer/ResponseReason.java +++ b/src/main/java/de/financer/ResponseReason.java @@ -29,7 +29,8 @@ public enum ResponseReason { MISSING_TRANSACTION_ID(HttpStatus.INTERNAL_SERVER_ERROR), INVALID_TRANSACTION_ID(HttpStatus.INTERNAL_SERVER_ERROR), TRANSACTION_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR), - ACCOUNT_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR); + ACCOUNT_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR), + DUPLICATE_ACCOUNT_KEY(HttpStatus.INTERNAL_SERVER_ERROR); private HttpStatus httpStatus; diff --git a/src/main/resources/i18n/message.properties b/src/main/resources/i18n/message.properties index b39ad02..791ee78 100644 --- a/src/main/resources/i18n/message.properties +++ b/src/main/resources/i18n/message.properties @@ -143,4 +143,5 @@ financer.error-message.RECURRING_TRANSACTION_NOT_FOUND=The recurring transaction financer.error-message.MISSING_TRANSACTION_ID=No transaction entered! financer.error-message.INVALID_TRANSACTION_ID=The transaction is not valid! financer.error-message.TRANSACTION_NOT_FOUND=The transaction could not be found! -financer.error-message.ACCOUNT_NOT_FOUND=The account could not be found! \ No newline at end of file +financer.error-message.ACCOUNT_NOT_FOUND=The account could not be found! +financer.error-message.DUPLICATE_ACCOUNT_KEY=An account with the given key already exists! \ No newline at end of file From 22c68215b9351097fd73ec1771cadb17b3dba278 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Tue, 14 May 2019 20:19:37 +0200 Subject: [PATCH 15/32] financer release version 4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fecb18a..87ec660 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-web-client</artifactId> <!-- Also increase the parallelDeploymentVersion --> - <version>4-SNAPSHOT</version> + <version>4</version> <packaging>${packaging.type}</packaging> <description>The web client part of the financer application - a simple app to manage your personal finances</description> <name>financer-web-client</name> From fa4d32796ade3f84959c73b7214351d25fa54a96 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Tue, 14 May 2019 20:20:11 +0200 Subject: [PATCH 16/32] Prepare next development iteration for v5 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 87ec660..108a4a2 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-web-client</artifactId> <!-- Also increase the parallelDeploymentVersion --> - <version>4</version> + <version>5-SNAPSHOT</version> <packaging>${packaging.type}</packaging> <description>The web client part of the financer application - a simple app to manage your personal finances</description> <name>financer-web-client</name> @@ -29,7 +29,7 @@ <!-- Property to define the parallel deployment version. See e.g. https://tomcat.apache.org/tomcat-8.5-doc/config/context.html Should be the same as the project.version but padded with zeros to six digits. --> - <parallelDeploymentVersion>000004</parallelDeploymentVersion> + <parallelDeploymentVersion>000005</parallelDeploymentVersion> <!-- Property to define the final name of the build artifact. This used in combination with the build-war profile to change the .war file name to e.g. financer#00004.war so that the application is accessible via the /financer From 2d2774179f4916ae9b61e3353748be68bf1d840c Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Wed, 15 May 2019 21:09:03 +0200 Subject: [PATCH 17/32] Adjust sorting order of accounts and transactions Accounts are primarily sorted by a synthetic sort oder and then by their ID. Transactions are primarily sorted by their date and then by their ID. --- .../de/financer/controller/AccountController.java | 8 ++++---- .../financer/controller/TransactionController.java | 4 ++-- .../java/de/financer/util/ControllerUtils.java | 4 ++-- ...rator.java => AccountByTypeByIdComparator.java} | 6 ++++-- .../TransactionByDateByIdDescComparator.java | 14 ++++++++++++++ .../comparator/TransactionByDateComparator.java | 12 ------------ 6 files changed, 26 insertions(+), 22 deletions(-) rename src/main/java/de/financer/util/comparator/{AccountByTypeComparator.java => AccountByTypeByIdComparator.java} (77%) create mode 100644 src/main/java/de/financer/util/comparator/TransactionByDateByIdDescComparator.java delete mode 100644 src/main/java/de/financer/util/comparator/TransactionByDateComparator.java diff --git a/src/main/java/de/financer/controller/AccountController.java b/src/main/java/de/financer/controller/AccountController.java index 6a39e6e..a1633cf 100644 --- a/src/main/java/de/financer/controller/AccountController.java +++ b/src/main/java/de/financer/controller/AccountController.java @@ -7,7 +7,7 @@ import de.financer.form.NewAccountForm; import de.financer.model.*; import de.financer.util.ControllerUtils; import de.financer.util.TransactionUtils; -import de.financer.util.comparator.TransactionByDateComparator; +import de.financer.util.comparator.TransactionByDateByIdDescComparator; import org.apache.commons.collections4.IterableUtils; import org.apache.commons.lang3.BooleanUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -83,7 +83,7 @@ public class AccountController { List<Transaction> transactions = IterableUtils.toList(transactionResponse.getBody()); - transactions.sort(new TransactionByDateComparator()); + transactions.sort(new TransactionByDateByIdDescComparator()); transactions.stream().forEach((t) -> TransactionUtils.adjustAmount(t, account)); model.addAttribute("account", account); @@ -112,7 +112,7 @@ public class AccountController { List<Transaction> transactions = IterableUtils.toList(transactionResponse.getBody()); - transactions.sort(new TransactionByDateComparator()); + transactions.sort(new TransactionByDateByIdDescComparator()); transactions.stream().forEach((t) -> TransactionUtils.adjustAmount(t, account)); model.addAttribute("account", account); @@ -145,7 +145,7 @@ public class AccountController { List<Transaction> transactions = IterableUtils.toList(transactionResponse.getBody()); - transactions.sort(new TransactionByDateComparator()); + transactions.sort(new TransactionByDateByIdDescComparator()); transactions.stream().forEach((t) -> TransactionUtils.adjustAmount(t, account)); model.addAttribute("account", account); diff --git a/src/main/java/de/financer/controller/TransactionController.java b/src/main/java/de/financer/controller/TransactionController.java index edce268..da0eb95 100644 --- a/src/main/java/de/financer/controller/TransactionController.java +++ b/src/main/java/de/financer/controller/TransactionController.java @@ -12,7 +12,7 @@ import de.financer.model.AccountStatus; import de.financer.model.Transaction; import de.financer.util.ControllerUtils; import de.financer.util.TransactionUtils; -import de.financer.util.comparator.TransactionByDateComparator; +import de.financer.util.comparator.TransactionByDateByIdDescComparator; import org.apache.commons.collections4.IterableUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; @@ -84,7 +84,7 @@ public class TransactionController { List<Transaction> transactions = IterableUtils.toList(transactionResponse.getBody()); - transactions.sort(new TransactionByDateComparator()); + transactions.sort(new TransactionByDateByIdDescComparator()); transactions.stream().forEach((t) -> TransactionUtils.adjustAmount(t, account)); model.addAttribute("account", account); diff --git a/src/main/java/de/financer/util/ControllerUtils.java b/src/main/java/de/financer/util/ControllerUtils.java index 64ce300..6843801 100644 --- a/src/main/java/de/financer/util/ControllerUtils.java +++ b/src/main/java/de/financer/util/ControllerUtils.java @@ -5,7 +5,7 @@ import de.financer.controller.Function; import de.financer.model.Account; import de.financer.model.AccountStatus; import de.financer.model.RecurringTransaction; -import de.financer.util.comparator.AccountByTypeComparator; +import de.financer.util.comparator.AccountByTypeByIdComparator; import org.apache.commons.collections4.IterableUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.ui.Model; @@ -28,7 +28,7 @@ public class ControllerUtils { return IterableUtils.toList(accounts).stream() .filter((acc) -> AccountStatus.OPEN .equals(acc.getStatus()) || showClosed) - .sorted(new AccountByTypeComparator()) + .sorted(new AccountByTypeByIdComparator()) .collect(Collectors.toList()); } diff --git a/src/main/java/de/financer/util/comparator/AccountByTypeComparator.java b/src/main/java/de/financer/util/comparator/AccountByTypeByIdComparator.java similarity index 77% rename from src/main/java/de/financer/util/comparator/AccountByTypeComparator.java rename to src/main/java/de/financer/util/comparator/AccountByTypeByIdComparator.java index 4723d9c..cf63757 100644 --- a/src/main/java/de/financer/util/comparator/AccountByTypeComparator.java +++ b/src/main/java/de/financer/util/comparator/AccountByTypeByIdComparator.java @@ -6,7 +6,7 @@ import de.financer.model.AccountType; import java.util.Comparator; import java.util.Map; -public class AccountByTypeComparator implements Comparator<Account> { +public class AccountByTypeByIdComparator implements Comparator<Account> { //@formatter:off private static final Map<AccountType, Integer> SORT_ORDER = Map.of(AccountType.BANK, 1, AccountType.CASH, 2, @@ -18,6 +18,8 @@ public class AccountByTypeComparator implements Comparator<Account> { @Override public int compare(Account o1, Account o2) { - return SORT_ORDER.get(o1.getType()).compareTo(SORT_ORDER.get(o2.getType())); + int typeOrder = SORT_ORDER.get(o1.getType()).compareTo(SORT_ORDER.get(o2.getType())); + + return typeOrder != 0 ? typeOrder : o1.getId().compareTo(o2.getId()); } } diff --git a/src/main/java/de/financer/util/comparator/TransactionByDateByIdDescComparator.java b/src/main/java/de/financer/util/comparator/TransactionByDateByIdDescComparator.java new file mode 100644 index 0000000..3ae2a9d --- /dev/null +++ b/src/main/java/de/financer/util/comparator/TransactionByDateByIdDescComparator.java @@ -0,0 +1,14 @@ +package de.financer.util.comparator; + +import de.financer.model.Transaction; + +import java.util.Comparator; + +public class TransactionByDateByIdDescComparator implements Comparator<Transaction> { + @Override + public int compare(Transaction o1, Transaction o2) { + int dateOrder = o1.getDate().compareTo(o2.getDate()) * -1; + + return dateOrder != 0 ? dateOrder : o1.getId().compareTo(o2.getId()) * -1; + } +} diff --git a/src/main/java/de/financer/util/comparator/TransactionByDateComparator.java b/src/main/java/de/financer/util/comparator/TransactionByDateComparator.java deleted file mode 100644 index c445fc8..0000000 --- a/src/main/java/de/financer/util/comparator/TransactionByDateComparator.java +++ /dev/null @@ -1,12 +0,0 @@ -package de.financer.util.comparator; - -import de.financer.model.Transaction; - -import java.util.Comparator; - -public class TransactionByDateComparator implements Comparator<Transaction> { - @Override - public int compare(Transaction o1, Transaction o2) { - return o1.getDate().compareTo(o2.getDate()) * -1; - } -} From 8fb0e8dc38facb5631281c3b5db459cba5b84c81 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 19 May 2019 11:58:39 +0200 Subject: [PATCH 18/32] financer release version 5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 108a4a2..9b7f040 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-web-client</artifactId> <!-- Also increase the parallelDeploymentVersion --> - <version>5-SNAPSHOT</version> + <version>5</version> <packaging>${packaging.type}</packaging> <description>The web client part of the financer application - a simple app to manage your personal finances</description> <name>financer-web-client</name> From 4b3567fa82e771d8b3efacaaf77cb5e3d090b8bc Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 19 May 2019 11:59:30 +0200 Subject: [PATCH 19/32] Prepare next development iteration for v6 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9b7f040..bf9a762 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-web-client</artifactId> <!-- Also increase the parallelDeploymentVersion --> - <version>5</version> + <version>6-SNAPSHOT</version> <packaging>${packaging.type}</packaging> <description>The web client part of the financer application - a simple app to manage your personal finances</description> <name>financer-web-client</name> @@ -29,7 +29,7 @@ <!-- Property to define the parallel deployment version. See e.g. https://tomcat.apache.org/tomcat-8.5-doc/config/context.html Should be the same as the project.version but padded with zeros to six digits. --> - <parallelDeploymentVersion>000005</parallelDeploymentVersion> + <parallelDeploymentVersion>000006</parallelDeploymentVersion> <!-- Property to define the final name of the build artifact. This used in combination with the build-war profile to change the .war file name to e.g. financer#00004.war so that the application is accessible via the /financer From 866b54a8bf9f17ae160b68eda184a110ffd568ad Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Mon, 10 Jun 2019 02:29:43 +0200 Subject: [PATCH 20/32] Add flag to control whether to remind about maturity of rec. transaction --- .../controller/RecurringTransactionController.java | 3 ++- .../de/financer/form/NewRecurringTransactionForm.java | 9 +++++++++ .../java/de/financer/model/RecurringTransaction.java | 9 +++++++++ src/main/resources/i18n/message.properties | 1 + src/main/resources/i18n/message_de_DE.properties | 3 ++- .../recurringTransaction/newRecurringTransaction.html | 2 ++ 6 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/financer/controller/RecurringTransactionController.java b/src/main/java/de/financer/controller/RecurringTransactionController.java index 68417ba..c3dab13 100644 --- a/src/main/java/de/financer/controller/RecurringTransactionController.java +++ b/src/main/java/de/financer/controller/RecurringTransactionController.java @@ -50,7 +50,8 @@ public class RecurringTransactionController { .queryParam("lastOccurrence", ControllerUtils.formatDate(this.financerConfig, form.getLastOccurrence())) .queryParam("holidayWeekendType", form.getHolidayWeekendType()) .queryParam("intervalType", form.getIntervalType()) - .queryParam("description", form.getDescription()); + .queryParam("description", form.getDescription()) + .queryParam("remind", form.getRemind()); final ResponseEntity<String> response = new StringTemplate().exchange(builder); diff --git a/src/main/java/de/financer/form/NewRecurringTransactionForm.java b/src/main/java/de/financer/form/NewRecurringTransactionForm.java index 3f7d419..a077eb0 100644 --- a/src/main/java/de/financer/form/NewRecurringTransactionForm.java +++ b/src/main/java/de/financer/form/NewRecurringTransactionForm.java @@ -9,6 +9,7 @@ public class NewRecurringTransactionForm { private String lastOccurrence; private String intervalType; private String holidayWeekendType; + private Boolean remind; public String getFromAccountKey() { return fromAccountKey; @@ -73,4 +74,12 @@ public class NewRecurringTransactionForm { public void setHolidayWeekendType(String holidayWeekendType) { this.holidayWeekendType = holidayWeekendType; } + + public Boolean getRemind() { + return remind; + } + + public void setRemind(Boolean remind) { + this.remind = remind; + } } diff --git a/src/main/java/de/financer/model/RecurringTransaction.java b/src/main/java/de/financer/model/RecurringTransaction.java index 417596f..a2d486a 100644 --- a/src/main/java/de/financer/model/RecurringTransaction.java +++ b/src/main/java/de/financer/model/RecurringTransaction.java @@ -12,6 +12,7 @@ public class RecurringTransaction { private LocalDate firstOccurrence; private LocalDate lastOccurrence; private HolidayWeekendType holidayWeekendType; + private boolean remind; public Long getId() { return id; @@ -80,4 +81,12 @@ public class RecurringTransaction { public void setIntervalType(IntervalType intervalType) { this.intervalType = intervalType; } + + public boolean isRemind() { + return remind; + } + + public void setRemind(boolean remind) { + this.remind = remind; + } } diff --git a/src/main/resources/i18n/message.properties b/src/main/resources/i18n/message.properties index 791ee78..daf3ce3 100644 --- a/src/main/resources/i18n/message.properties +++ b/src/main/resources/i18n/message.properties @@ -43,6 +43,7 @@ financer.recurring-transaction-new.label.last-occurrence=Last occurrence\: financer.recurring-transaction-new.label.interval-type=Interval\: financer.recurring-transaction-new.label.holiday-weekend-type=Holiday/weekend rule\: financer.recurring-transaction-new.label.description=Description\: +financer.recurring-transaction-new.label.remind=Remind if due\: financer.recurring-transaction-new.submit=Create recurring transaction financer.recurring-transaction-new.account-type.BANK={0}|Bank|{1} financer.recurring-transaction-new.account-type.CASH={0}|Cash|{1} diff --git a/src/main/resources/i18n/message_de_DE.properties b/src/main/resources/i18n/message_de_DE.properties index 7c99954..d91e4b9 100644 --- a/src/main/resources/i18n/message_de_DE.properties +++ b/src/main/resources/i18n/message_de_DE.properties @@ -43,6 +43,7 @@ financer.recurring-transaction-new.label.last-occurrence=Letztes Auftreten\: financer.recurring-transaction-new.label.interval-type=Intervall\: financer.recurring-transaction-new.label.holiday-weekend-type=Feiertag-/Wochenendregel\: financer.recurring-transaction-new.label.description=Beschreibung\: +financer.recurring-transaction-new.label.remind=Erinnern wenn f\u00E4llig\: financer.recurring-transaction-new.submit=Wiederkehrende Buchung erstellen financer.recurring-transaction-new.account-type.BANK={0}|Bank|{1} financer.recurring-transaction-new.account-type.CASH={0}|Bar|{1} @@ -67,7 +68,7 @@ financer.recurring-transaction-list.table-header.actions=Aktionen financer.recurring-transaction-list.table.actions.createTransaction=Erstelle Buchung financer.recurring-transaction-list.table.actions.createTransactionWithAmount=Erstelle Buchung mit Betrag financer.recurring-transaction-list.table.actions.editRecurringTransaction=Bearbeiten -financer.recurring-transaction-list.table.actions.deleteRecurringTransaction=Löschen +financer.recurring-transaction-list.table.actions.deleteRecurringTransaction=L\u00F6schen financer.account-details.title=financer\: Kontodetails financer.account-details.available-actions=Verf\u00FCgbare Aktionen\: diff --git a/src/main/resources/templates/recurringTransaction/newRecurringTransaction.html b/src/main/resources/templates/recurringTransaction/newRecurringTransaction.html index 9fad278..3d1c603 100644 --- a/src/main/resources/templates/recurringTransaction/newRecurringTransaction.html +++ b/src/main/resources/templates/recurringTransaction/newRecurringTransaction.html @@ -39,6 +39,8 @@ </select> <label for="inputDescription" th:text="#{financer.recurring-transaction-new.label.description}"/> <input type="text" id="inputDescription" th:field="*{description}"/> + <label for="inputRemind" th:text="#{financer.recurring-transaction-new.label.remind}" /> + <input type="checkbox" id="inputRemind" th:field="*{remind}" /> <input type="submit" th:value="#{financer.recurring-transaction-new.submit}"/> </form> <div th:replace="includes/footer :: footer"/> From ecd55b8638879561f00af9a8928a8c664cb9ed73 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Mon, 10 Jun 2019 12:40:49 +0200 Subject: [PATCH 21/32] Add parent module financer-parent Rework POM structure: - Move general profiles to new parent - Add dependency management for project dependencies - Move general properties to new parent - Alo remove version numbers from child modules, so versioning is now done via parent --- pom.xml | 66 ++++----------------------------------------------------- 1 file changed, 4 insertions(+), 62 deletions(-) diff --git a/pom.xml b/pom.xml index bf9a762..7166243 100644 --- a/pom.xml +++ b/pom.xml @@ -5,31 +5,21 @@ <modelVersion>4.0.0</modelVersion> <parent> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-parent</artifactId> - <version>2.1.2.RELEASE</version> - <relativePath/> + <groupId>de.77zzcx7.financer</groupId> + <artifactId>financer-parent</artifactId> + <version>6-SNAPSHOT</version> + <relativePath>../financer-parent</relativePath> </parent> <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-web-client</artifactId> - <!-- Also increase the parallelDeploymentVersion --> - <version>6-SNAPSHOT</version> <packaging>${packaging.type}</packaging> <description>The web client part of the financer application - a simple app to manage your personal finances</description> <name>financer-web-client</name> <properties> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <maven.compiler.source>1.9</maven.compiler.source> - <maven.compiler.target>1.9</maven.compiler.target> - <java.version>1.9</java.version> <packaging.type>jar</packaging.type> <activeProfiles>dev</activeProfiles> - <!-- Property to define the parallel deployment version. - See e.g. https://tomcat.apache.org/tomcat-8.5-doc/config/context.html - Should be the same as the project.version but padded with zeros to six digits. --> - <parallelDeploymentVersion>000006</parallelDeploymentVersion> <!-- Property to define the final name of the build artifact. This used in combination with the build-war profile to change the .war file name to e.g. financer#00004.war so that the application is accessible via the /financer @@ -57,7 +47,6 @@ <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> - <version>4.3</version> </dependency> <!-- Misc dependencies --> @@ -77,58 +66,11 @@ <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> - <version>4.12</version> <scope>test</scope> </dependency> </dependencies> - <build> - <finalName>${project.artifactId}</finalName> - <plugins> - <plugin> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-maven-plugin</artifactId> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <excludes> - <exclude>**/*IntegrationTest</exclude> - </excludes> - </configuration> - </plugin> - </plugins> - </build> - <profiles> - <profile> - <id>integration-tests</id> - <build> - <plugins> - <plugin> - <artifactId>maven-surefire-plugin</artifactId> - <executions> - <execution> - <phase>integration-test</phase> - <goals> - <goal>test</goal> - </goals> - <configuration> - <excludes> - <exclude>none</exclude> - </excludes> - <includes> - <include>**/*IntegrationTest</include> - </includes> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - <profile> <id>build-war</id> <properties> From d7366f15710d34831ea094ae4c9777fe12f5d2a9 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Mon, 10 Jun 2019 21:26:46 +0200 Subject: [PATCH 22/32] Update .gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b3cec76..007ac1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ financer-web-client.log* -.attach* \ No newline at end of file +.attach* +financer-server.log* +*.iml \ No newline at end of file From f7816db0968669487628068b2b69d4daa96aae79 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Mon, 10 Jun 2019 22:21:23 +0200 Subject: [PATCH 23/32] Set default of remind to true --- .../financer/controller/RecurringTransactionController.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/financer/controller/RecurringTransactionController.java b/src/main/java/de/financer/controller/RecurringTransactionController.java index c3dab13..9819489 100644 --- a/src/main/java/de/financer/controller/RecurringTransactionController.java +++ b/src/main/java/de/financer/controller/RecurringTransactionController.java @@ -29,11 +29,14 @@ public class RecurringTransactionController { @GetMapping("/newRecurringTransaction") public String newRecurringTransaction(Model model) { final ResponseEntity<Iterable<Account>> response = new GetAllAccountsTemplate().exchange(this.financerConfig); + final NewRecurringTransactionForm form = new NewRecurringTransactionForm(); + + form.setRemind(Boolean.TRUE); model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(response.getBody())); model.addAttribute("intervalTypes", IntervalType.valueList()); model.addAttribute("holidayWeekendTypes", HolidayWeekendType.valueList()); - model.addAttribute("form", new NewRecurringTransactionForm()); + model.addAttribute("form", form); ControllerUtils.addVersionAttribute(model, this.financerConfig); return "recurringTransaction/newRecurringTransaction"; From 4825b82066dfb54f7b94eac6140acb854f4aed66 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Mon, 10 Jun 2019 22:21:59 +0200 Subject: [PATCH 24/32] financer release version 6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7166243..c423f22 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-parent</artifactId> - <version>6-SNAPSHOT</version> + <version>6</version> <relativePath>../financer-parent</relativePath> </parent> From ac1988ad2022fb87da75e8be384d0f456eb538c4 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Mon, 10 Jun 2019 22:23:46 +0200 Subject: [PATCH 25/32] Prepare next development iteration for v7 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c423f22..7e5c6cf 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-parent</artifactId> - <version>6</version> + <version>7-SNAPSHOT</version> <relativePath>../financer-parent</relativePath> </parent> From eaf11637a34401bcd46e2a7168d77b4ea4f9e50f Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 16 Jun 2019 01:34:55 +0200 Subject: [PATCH 26/32] Introduce new feature 'Account groups' Account groups are a simple way to group accounts. Currently the design of the group is very sparse and supports only a name. However, as this feature is a stepping stone for the much bigger 'Reports' feature this will most likely be subject to change. With this change new accounts are also required to get assigned to a group. Also fix some translations. --- src/main/java/de/financer/ResponseReason.java | 5 +- .../controller/AccountController.java | 13 ++++- .../controller/AccountGroupController.java | 48 +++++++++++++++++++ .../java/de/financer/controller/Function.java | 3 ++ .../template/GetAllAccountGroupsTemplate.java | 16 +++++++ .../java/de/financer/form/NewAccountForm.java | 9 ++++ .../de/financer/form/NewAccountGroupForm.java | 13 +++++ src/main/java/de/financer/model/Account.java | 9 ++++ .../java/de/financer/model/AccountGroup.java | 18 +++++++ .../de/financer/util/ControllerUtils.java | 8 ++++ src/main/resources/i18n/message.properties | 17 +++++-- .../resources/i18n/message_de_DE.properties | 13 ++++- src/main/resources/static/css/main.css | 15 +++++- .../templates/account/accountDetails.html | 6 ++- .../templates/account/accountOverview.html | 31 +++++++----- .../templates/account/newAccount.html | 6 ++- .../accountGroup/newAccountGroup.html | 19 ++++++++ 17 files changed, 225 insertions(+), 24 deletions(-) create mode 100644 src/main/java/de/financer/controller/AccountGroupController.java create mode 100644 src/main/java/de/financer/controller/template/GetAllAccountGroupsTemplate.java create mode 100644 src/main/java/de/financer/form/NewAccountGroupForm.java create mode 100644 src/main/java/de/financer/model/AccountGroup.java create mode 100644 src/main/resources/templates/accountGroup/newAccountGroup.html diff --git a/src/main/java/de/financer/ResponseReason.java b/src/main/java/de/financer/ResponseReason.java index 01a1025..f73464b 100644 --- a/src/main/java/de/financer/ResponseReason.java +++ b/src/main/java/de/financer/ResponseReason.java @@ -7,7 +7,6 @@ public enum ResponseReason { OK(HttpStatus.OK), UNKNOWN_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), INVALID_ACCOUNT_TYPE(HttpStatus.INTERNAL_SERVER_ERROR), - INVALID_ACCOUNT_KEY(HttpStatus.INTERNAL_SERVER_ERROR), FROM_ACCOUNT_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR), TO_ACCOUNT_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR), FROM_AND_TO_ACCOUNT_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR), @@ -30,7 +29,9 @@ public enum ResponseReason { INVALID_TRANSACTION_ID(HttpStatus.INTERNAL_SERVER_ERROR), TRANSACTION_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR), ACCOUNT_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR), - DUPLICATE_ACCOUNT_KEY(HttpStatus.INTERNAL_SERVER_ERROR); + DUPLICATE_ACCOUNT_KEY(HttpStatus.INTERNAL_SERVER_ERROR), + DUPLICATE_ACCOUNT_GROUP_NAME(HttpStatus.INTERNAL_SERVER_ERROR), + ACCOUNT_GROUP_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR); private HttpStatus httpStatus; diff --git a/src/main/java/de/financer/controller/AccountController.java b/src/main/java/de/financer/controller/AccountController.java index a1633cf..8c69a8f 100644 --- a/src/main/java/de/financer/controller/AccountController.java +++ b/src/main/java/de/financer/controller/AccountController.java @@ -45,7 +45,11 @@ public class AccountController { @GetMapping("/newAccount") public String newAccount(Model model) { - model.addAttribute("accounttypes", AccountType.valueList()); + final ResponseEntity<Iterable<AccountGroup>> accountGroupResponse = new GetAllAccountGroupsTemplate() + .exchange(this.financerConfig); + + model.addAttribute("accountGroups", ControllerUtils.sortAccountGroups(accountGroupResponse.getBody())); + model.addAttribute("accountTypes", AccountType.valueList()); model.addAttribute("form", new NewAccountForm()); ControllerUtils.addVersionAttribute(model, this.financerConfig); @@ -57,14 +61,19 @@ public class AccountController { final UriComponentsBuilder builder = UriComponentsBuilder .fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.ACC_CREATE_ACCOUNT)) .queryParam("key", form.getKey()) + .queryParam("accountGroupName", form.getGroup()) .queryParam("type", form.getType()); final ResponseEntity<String> response = new StringTemplate().exchange(builder); final ResponseReason responseReason = ResponseReason.fromResponseEntity(response); if (!ResponseReason.OK.equals(responseReason)) { + final ResponseEntity<Iterable<AccountGroup>> accountGroupResponse = new GetAllAccountGroupsTemplate() + .exchange(this.financerConfig); + model.addAttribute("form", form); - model.addAttribute("accounttypes", AccountType.valueList()); + model.addAttribute("accountGroups", ControllerUtils.sortAccountGroups(accountGroupResponse.getBody())); + model.addAttribute("accountTypes", AccountType.valueList()); model.addAttribute("errorMessage", responseReason.name()); ControllerUtils.addVersionAttribute(model, this.financerConfig); diff --git a/src/main/java/de/financer/controller/AccountGroupController.java b/src/main/java/de/financer/controller/AccountGroupController.java new file mode 100644 index 0000000..047d330 --- /dev/null +++ b/src/main/java/de/financer/controller/AccountGroupController.java @@ -0,0 +1,48 @@ +package de.financer.controller; + +import de.financer.ResponseReason; +import de.financer.config.FinancerConfig; +import de.financer.controller.template.StringTemplate; +import de.financer.form.NewAccountGroupForm; +import de.financer.util.ControllerUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.util.UriComponentsBuilder; + +@Controller +public class AccountGroupController { + @Autowired + private FinancerConfig financerConfig; + + @GetMapping("/newAccountGroup") + public String newAccountGroup(Model model) { + model.addAttribute("form", new NewAccountGroupForm()); + ControllerUtils.addVersionAttribute(model, this.financerConfig); + + return "accountGroup/newAccountGroup"; + } + + @PostMapping("/saveAccountGroup") + public String saveAccontGroup(NewAccountGroupForm form, Model model) { + final UriComponentsBuilder builder = UriComponentsBuilder + .fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.ACC_GP_CREATE_ACCOUNT_GROUP)) + .queryParam("name", form.getName()); + + final ResponseEntity<String> response = new StringTemplate().exchange(builder); + final ResponseReason responseReason = ResponseReason.fromResponseEntity(response); + + if (!ResponseReason.OK.equals(responseReason)) { + model.addAttribute("form", form); + model.addAttribute("errorMessage", responseReason.name()); + ControllerUtils.addVersionAttribute(model, this.financerConfig); + + return "accountGroup/newAccountGroup"; + } + + return "redirect:/accountOverview"; + } +} diff --git a/src/main/java/de/financer/controller/Function.java b/src/main/java/de/financer/controller/Function.java index e97bdcb..602a4fd 100644 --- a/src/main/java/de/financer/controller/Function.java +++ b/src/main/java/de/financer/controller/Function.java @@ -7,6 +7,9 @@ public enum Function { ACC_CLOSE_ACCOUNT("accounts/closeAccount"), ACC_OPEN_ACCOUNT("accounts/openAccount"), + ACC_GP_CREATE_ACCOUNT_GROUP("accountGroups/createAccountGroup"), + ACC_GP_GET_ALL("accountGroups/getAll"), + TR_GET_ALL("transactions/getAll"), TR_GET_ALL_FOR_ACCOUNT("transactions/getAllForAccount"), TR_CREATE_TRANSACTION("transactions/createTransaction"), diff --git a/src/main/java/de/financer/controller/template/GetAllAccountGroupsTemplate.java b/src/main/java/de/financer/controller/template/GetAllAccountGroupsTemplate.java new file mode 100644 index 0000000..46b2ecc --- /dev/null +++ b/src/main/java/de/financer/controller/template/GetAllAccountGroupsTemplate.java @@ -0,0 +1,16 @@ +package de.financer.controller.template; + +import de.financer.config.FinancerConfig; +import de.financer.controller.Function; +import de.financer.model.AccountGroup; +import de.financer.util.ControllerUtils; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.ResponseEntity; + +public class GetAllAccountGroupsTemplate { + public ResponseEntity<Iterable<AccountGroup>> exchange(FinancerConfig financerConfig) { + return new FinancerRestTemplate<Iterable<AccountGroup>>().exchange(ControllerUtils + .buildUrl(financerConfig, Function.ACC_GP_GET_ALL), new ParameterizedTypeReference<Iterable<AccountGroup>>() { + }); + } +} diff --git a/src/main/java/de/financer/form/NewAccountForm.java b/src/main/java/de/financer/form/NewAccountForm.java index fd3218d..dea8a7d 100644 --- a/src/main/java/de/financer/form/NewAccountForm.java +++ b/src/main/java/de/financer/form/NewAccountForm.java @@ -3,6 +3,7 @@ package de.financer.form; public class NewAccountForm { private String key; private String type; + private String group; public String getKey() { return key; @@ -19,4 +20,12 @@ public class NewAccountForm { public void setType(String type) { this.type = type; } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } } diff --git a/src/main/java/de/financer/form/NewAccountGroupForm.java b/src/main/java/de/financer/form/NewAccountGroupForm.java new file mode 100644 index 0000000..4872398 --- /dev/null +++ b/src/main/java/de/financer/form/NewAccountGroupForm.java @@ -0,0 +1,13 @@ +package de.financer.form; + +public class NewAccountGroupForm { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/de/financer/model/Account.java b/src/main/java/de/financer/model/Account.java index 0e4101e..50d0ff3 100644 --- a/src/main/java/de/financer/model/Account.java +++ b/src/main/java/de/financer/model/Account.java @@ -6,6 +6,7 @@ public class Account { private AccountType type; private AccountStatus status; private Long currentBalance; + private AccountGroup accountGroup; public Long getId() { return id; @@ -42,4 +43,12 @@ public class Account { public void setCurrentBalance(Long currentBalance) { this.currentBalance = currentBalance; } + + public AccountGroup getAccountGroup() { + return accountGroup; + } + + public void setAccountGroup(AccountGroup accountGroup) { + this.accountGroup = accountGroup; + } } diff --git a/src/main/java/de/financer/model/AccountGroup.java b/src/main/java/de/financer/model/AccountGroup.java new file mode 100644 index 0000000..8e0a90f --- /dev/null +++ b/src/main/java/de/financer/model/AccountGroup.java @@ -0,0 +1,18 @@ +package de.financer.model; + +public class AccountGroup { + private Long id; + private String name; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/de/financer/util/ControllerUtils.java b/src/main/java/de/financer/util/ControllerUtils.java index 6843801..d167e6c 100644 --- a/src/main/java/de/financer/util/ControllerUtils.java +++ b/src/main/java/de/financer/util/ControllerUtils.java @@ -3,6 +3,7 @@ package de.financer.util; import de.financer.config.FinancerConfig; import de.financer.controller.Function; import de.financer.model.Account; +import de.financer.model.AccountGroup; import de.financer.model.AccountStatus; import de.financer.model.RecurringTransaction; import de.financer.util.comparator.AccountByTypeByIdComparator; @@ -12,6 +13,7 @@ import org.springframework.ui.Model; import java.time.LocalDate; import java.time.format.DateTimeFormatter; +import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; @@ -32,6 +34,12 @@ public class ControllerUtils { .collect(Collectors.toList()); } + public static List<AccountGroup> sortAccountGroups(Iterable<AccountGroup> accountGroups) { + return IterableUtils.toList(accountGroups).stream() + .sorted(Comparator.comparing(AccountGroup::getName)) + .collect(Collectors.toList()); + } + public static String formatDate(FinancerConfig financerConfig, String dateFromForm) { if (StringUtils.isEmpty(dateFromForm)) { return null; diff --git a/src/main/resources/i18n/message.properties b/src/main/resources/i18n/message.properties index daf3ce3..9d7b159 100644 --- a/src/main/resources/i18n/message.properties +++ b/src/main/resources/i18n/message.properties @@ -6,11 +6,13 @@ financer.account-overview.available-actions.create-account=Create new account financer.account-overview.available-actions.create-transaction=Create new transaction financer.account-overview.available-actions.create-recurring-transaction=Create new recurring transaction 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.status=Status\: financer.account-overview.status.recurring-transaction-due-today=Recurring transactions due today\: financer.account-overview.status.recurring-transaction-active=Active recurring transactions\: financer.account-overview.table-header.id=ID -financer.account-overview.table-header.key=Key +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.type=Type financer.account-overview.table-header.status=Status @@ -18,8 +20,13 @@ financer.account-overview.table-header.status=Status financer.account-new.title=financer\: create new account financer.account-new.label.key=Key\: financer.account-new.label.type=Type\: +financer.account-new.label.group=Group\: financer.account-new.submit=Create account +financer.account-group-new.title=financer\: create new account group +financer.account-group-new.label.name=Name\: +financer.account-group-new.submit=Create account group + financer.transaction-new.title=financer\: create new transaction financer.transaction-new.label.from-account=From account\: financer.transaction-new.label.to-account=To account\: @@ -83,7 +90,8 @@ financer.account-details.table-header.amount=Amount financer.account-details.table-header.description=Description financer.account-details.table-header.byRecurring=Recurring? financer.account-details.details.type=Type\: -financer.account-details.details.balance=Current Balance\: +financer.account-details.details.balance=Current balance\: +financer.account-details.details.group=Group\: financer.account-details.table-header.actions=Actions financer.account-details.table.actions.deleteTransaction=Delete @@ -114,6 +122,7 @@ financer.account-status.CLOSED=Closed financer.heading.transaction-new=financer\: create new transaction financer.heading.recurring-transaction-new=financer\: create new recurring transaction financer.heading.account-new=financer\: create new account +financer.heading.account-group-new=financer\: create new account group financer.heading.account-overview=financer\: overview financer.heading.account-details=financer\: account details for {0} financer.heading.recurring-transaction-list.dueToday=financer\: recurring transactions due today @@ -145,4 +154,6 @@ financer.error-message.MISSING_TRANSACTION_ID=No transaction entered! financer.error-message.INVALID_TRANSACTION_ID=The transaction is not valid! financer.error-message.TRANSACTION_NOT_FOUND=The transaction could not be found! financer.error-message.ACCOUNT_NOT_FOUND=The account could not be found! -financer.error-message.DUPLICATE_ACCOUNT_KEY=An account with the given key already exists! \ No newline at end of file +financer.error-message.DUPLICATE_ACCOUNT_KEY=An account with the given key already exists! +financer.error-message.DUPLICATE_ACCOUNT_GROUP_NAME=An account group with the given key already exists! +financer.error-message.ACCOUNT_GROUP_NOT_FOUND=The account group could not be found! \ No newline at end of file diff --git a/src/main/resources/i18n/message_de_DE.properties b/src/main/resources/i18n/message_de_DE.properties index d91e4b9..ddb1924 100644 --- a/src/main/resources/i18n/message_de_DE.properties +++ b/src/main/resources/i18n/message_de_DE.properties @@ -6,11 +6,13 @@ financer.account-overview.available-actions.create-account=Neues Konto erstellen financer.account-overview.available-actions.create-transaction=Neue Buchung erstellen financer.account-overview.available-actions.create-recurring-transaction=Neue wiederkehrende Buchung erstellen financer.account-overview.available-actions.recurring-transaction-all=Zeige alle wiederkehrende Buchungen +financer.account-overview.available-actions.create-account-group=Neue Konto-Gruppe erstellen 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\: financer.account-overview.table-header.id=ID -financer.account-overview.table-header.key=Schl\u00FCssel +financer.account-overview.table-header.key=Konto +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 @@ -18,7 +20,12 @@ financer.account-overview.table-header.status=Status financer.account-new.title=financer\: Neues Konto erstellen financer.account-new.label.key=Schl\u00FCssel\: financer.account-new.label.type=Typ\: -financer.account-new.submit=Account erstellen +financer.account-new.label.group=Gruppe\: +financer.account-new.submit=Konto erstellen + +financer.account-group-new.title=financer\: Neue Konto-Gruppe erstellen +financer.account-group-new.label.name=Name\: +financer.account-group-new.submit=Konto-Gruppe erstellen financer.transaction-new.title=financer\: Neue Buchung erstellen financer.transaction-new.label.from-account=Von Konto\: @@ -84,6 +91,7 @@ financer.account-details.table-header.description=Beschreibung financer.account-details.table-header.byRecurring=Wiederkehrend? financer.account-details.details.type=Typ\: financer.account-details.details.balance=Kontostand\: +financer.account-details.details.group=Gruppe\: financer.account-details.table-header.actions=Aktionen financer.account-details.table.actions.deleteTransaction=L\u00F6schen @@ -114,6 +122,7 @@ financer.account-status.CLOSED=Geschlossen financer.heading.transaction-new=financer\: Neue Buchung erstellen financer.heading.recurring-transaction-new=financer\: Neue wiederkehrende Buchung erstellen financer.heading.account-new=financer\: Neues Konto erstellen +financer.heading.account-group-new=financer\: Neue Konto-Gruppe erstellen financer.heading.account-overview=financer\: \u00DCbersicht financer.heading.account-details=financer\: Kontodetails f\u00FCr {0} financer.heading.recurring-transaction-list.dueToday=financer\: wiederkehrende Buchungen heute f\u00E4llig diff --git a/src/main/resources/static/css/main.css b/src/main/resources/static/css/main.css index d3ef84a..fc642c3 100644 --- a/src/main/resources/static/css/main.css +++ b/src/main/resources/static/css/main.css @@ -32,9 +32,19 @@ tr:hover { #new-recurring-transaction-form * { width: 100% !important; } + #action-container * { + display: block !important; + margin-bottom: 0.1em; + } } -#action-container *, +#action-container * { + display: inline-grid; + padding-inline-end: 0.2em; + margin-bottom: 0.2em; +} + +#account-details-action-container *, #recurring-transaction-list-table-actions-container *, #account-transaction-table-actions-container * { display: block; @@ -56,7 +66,8 @@ tr:hover { #new-account-form *, #new-transaction-form *, #new-recurring-transaction-form *, -#recurring-to-transaction-with-amount-form * { +#recurring-to-transaction-with-amount-form *, +#new-account-group-form * { display: block; margin-top: 1em; width: 20em; diff --git a/src/main/resources/templates/account/accountDetails.html b/src/main/resources/templates/account/accountDetails.html index 73cf347..e393186 100644 --- a/src/main/resources/templates/account/accountDetails.html +++ b/src/main/resources/templates/account/accountDetails.html @@ -18,8 +18,12 @@ <span th:text="#{financer.account-details.details.balance}"/> <span th:text="${#numbers.formatDecimal(account.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT')}"/> </div> + <div id="group-container"> + <span th:text="#{financer.account-details.details.group}"/> + <span th:text="${account.accountGroup?.name}"/> + </div> </div> -<div id="action-container"> +<div id="account-details-action-container"> <span th:text="#{financer.account-details.available-actions}"/> <a th:if="${!isClosed}" th:href="@{/closeAccount(key=${account.key})}" th:text="#{financer.account-details.available-actions.close-account}"/> diff --git a/src/main/resources/templates/account/accountOverview.html b/src/main/resources/templates/account/accountOverview.html index cb63874..3abcf48 100644 --- a/src/main/resources/templates/account/accountOverview.html +++ b/src/main/resources/templates/account/accountOverview.html @@ -19,24 +19,32 @@ <a th:href="@{/recurringTransactionActive}" th:text="${rtAllActiveCount}"/> </div> </div> +<span th:text="#{financer.account-overview.available-actions}"/> <div id="action-container"> - <span th:text="#{financer.account-overview.available-actions}"/> - <a th:if="${!showClosed}" th:href="@{'/accountOverview?showClosed=true'}" - th:text="#{financer.account-overview.available-actions.show-closed}"/> - <a th:if="${showClosed}" th:href="@{'/accountOverview'}" - th:text="#{financer.account-overview.available-actions.hide-closed}"/> - <a th:href="@{/newAccount}" th:text="#{financer.account-overview.available-actions.create-account}"/> - <a th:href="@{/newTransaction}" th:text="#{financer.account-overview.available-actions.create-transaction}"/> - <a th:href="@{/newRecurringTransaction}" - th:text="#{financer.account-overview.available-actions.create-recurring-transaction}"/> - <a th:href="@{/recurringTransactionAll}" - th:text="#{financer.account-overview.available-actions.recurring-transaction-all}"/> + <div id="action-container-sub-accounts"> + <a th:if="${!showClosed}" th:href="@{'/accountOverview?showClosed=true'}" + th:text="#{financer.account-overview.available-actions.show-closed}"/> + <a th:if="${showClosed}" th:href="@{'/accountOverview'}" + th:text="#{financer.account-overview.available-actions.hide-closed}"/> + <a th:href="@{/newAccount}" th:text="#{financer.account-overview.available-actions.create-account}"/> + <a th:href="@{/newAccountGroup}" th:text="#{financer.account-overview.available-actions.create-account-group}"/> + </div> + <div id="action-container-sub-transactions"> + <a th:href="@{/newTransaction}" th:text="#{financer.account-overview.available-actions.create-transaction}"/> + </div> + <div id="action-container-sub-recurring-transactions"> + <a th:href="@{/newRecurringTransaction}" + th:text="#{financer.account-overview.available-actions.create-recurring-transaction}"/> + <a th:href="@{/recurringTransactionAll}" + th:text="#{financer.account-overview.available-actions.recurring-transaction-all}"/> + </div> </div> <table id="account-overview-table"> <tr> <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.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}"/> </tr> @@ -46,6 +54,7 @@ <a th:href="@{/accountDetails(key=${acc.key})}" th:text="${acc.key}"/> </td> <td th:text="${#numbers.formatDecimal(acc.currentBalance/100D, 1, 'DEFAULT', 2, 'DEFAULT')}"/> + <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}}"/> </tr> diff --git a/src/main/resources/templates/account/newAccount.html b/src/main/resources/templates/account/newAccount.html index e26e3c4..7a10e30 100644 --- a/src/main/resources/templates/account/newAccount.html +++ b/src/main/resources/templates/account/newAccount.html @@ -14,7 +14,11 @@ <input type="text" id="inputKey" th:field="*{key}" /> <label for="selectType" th:text="#{financer.account-new.label.type}"/> <select id="selectType" th:field="*{type}"> - <option th:each="type : ${accounttypes}" th:value="${type}" th:text="#{'financer.account-type.' + ${type}}"/> + <option th:each="type : ${accountTypes}" th:value="${type}" th:text="#{'financer.account-type.' + ${type}}"/> + </select> + <label for="selectGroup" th:text="#{financer.account-new.label.group}"/> + <select id="selectGroup" th:field="*{group}"> + <option th:each="group : ${accountGroups}" th:value="${group.name}" th:text="${group.name}"/> </select> <input type="submit" th:value="#{financer.account-new.submit}" /> </form> diff --git a/src/main/resources/templates/accountGroup/newAccountGroup.html b/src/main/resources/templates/accountGroup/newAccountGroup.html new file mode 100644 index 0000000..d1644b5 --- /dev/null +++ b/src/main/resources/templates/accountGroup/newAccountGroup.html @@ -0,0 +1,19 @@ +<!DOCTYPE HTML> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <title th:text="#{financer.account-group-new.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}"> +</head> +<body> +<h1 th:text="#{financer.heading.account-group-new}" /> +<span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/> +<form id="new-account-group-form" action="#" th:action="@{/saveAccountGroup}" th:object="${form}" method="post"> + <label for="inputName" th:text="#{financer.account-group-new.label.name}"/> + <input type="text" id="inputName" th:field="*{name}" /> + <input type="submit" th:value="#{financer.account-group-new.submit}" /> +</form> +<div th:replace="includes/footer :: footer"/> +</body> +</html> \ No newline at end of file From 50c10fdb7826fce64d3e289b99c6350f00027554 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 16 Jun 2019 01:43:54 +0200 Subject: [PATCH 27/32] financer release version 7 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7e5c6cf..fc158e0 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-parent</artifactId> - <version>7-SNAPSHOT</version> + <version>7</version> <relativePath>../financer-parent</relativePath> </parent> From ff6557bc703ad6d855793288114eb7d3f7d4c310 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 16 Jun 2019 01:44:37 +0200 Subject: [PATCH 28/32] Prepare next development iteration for v8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fc158e0..d985a06 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-parent</artifactId> - <version>7</version> + <version>8-SNAPSHOT</version> <relativePath>../financer-parent</relativePath> </parent> From be74057b6ef22b23f4e15f66f805a038c74ff513 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 16 Jun 2019 20:24:55 +0200 Subject: [PATCH 29/32] Adjust padding of action container items --- src/main/resources/static/css/main.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/static/css/main.css b/src/main/resources/static/css/main.css index fc642c3..8f3eef5 100644 --- a/src/main/resources/static/css/main.css +++ b/src/main/resources/static/css/main.css @@ -40,7 +40,7 @@ tr:hover { #action-container * { display: inline-grid; - padding-inline-end: 0.2em; + padding-inline-end: 1em; margin-bottom: 0.2em; } From 53f1e0ad4908047cb2ad4f6aa80b82a98e89b2f6 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 16 Jun 2019 20:25:59 +0200 Subject: [PATCH 30/32] financer release version 8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d985a06..47912d5 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-parent</artifactId> - <version>8-SNAPSHOT</version> + <version>8</version> <relativePath>../financer-parent</relativePath> </parent> From 0d16aa7c0ada4b22d7a3544bd1d74007d6211942 Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Sun, 16 Jun 2019 20:26:28 +0200 Subject: [PATCH 31/32] Prepare next development iteration for v9 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 47912d5..6b810ca 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>de.77zzcx7.financer</groupId> <artifactId>financer-parent</artifactId> - <version>8</version> + <version>9-SNAPSHOT</version> <relativePath>../financer-parent</relativePath> </parent> From 7a8865bdf128f84c3422b22fa2f7b01205d4518e Mon Sep 17 00:00:00 2001 From: MK13 <marius@kleberonline.de> Date: Thu, 20 Jun 2019 14:38:20 +0200 Subject: [PATCH 32/32] Move content to subfolder to prepare for merge with -parent repository --- .gitignore => financer-web-client/.gitignore | 0 pom.xml => financer-web-client/pom.xml | 0 .../src}/main/java/de/financer/FinancerApplication.java | 0 .../src}/main/java/de/financer/ResponseReason.java | 0 .../src}/main/java/de/financer/config/FinancerConfig.java | 0 .../src}/main/java/de/financer/controller/AccountController.java | 0 .../main/java/de/financer/controller/AccountGroupController.java | 0 .../src}/main/java/de/financer/controller/Function.java | 0 .../de/financer/controller/RecurringTransactionController.java | 0 .../main/java/de/financer/controller/TransactionController.java | 0 .../controller/handler/NoExceptionResponseErrorHandler.java | 0 .../de/financer/controller/template/FinancerRestTemplate.java | 0 .../de/financer/controller/template/GetAccountByKeyTemplate.java | 0 .../financer/controller/template/GetAllAccountGroupsTemplate.java | 0 .../de/financer/controller/template/GetAllAccountsTemplate.java | 0 .../template/GetAllActiveRecurringTransactionsTemplate.java | 0 .../template/GetAllRecurringTransactionsDueTodayTemplate.java | 0 .../controller/template/GetAllRecurringTransactionsTemplate.java | 0 .../controller/template/GetAllTransactionsForAccountTemplate.java | 0 .../main/java/de/financer/controller/template/StringTemplate.java | 0 .../src}/main/java/de/financer/form/NewAccountForm.java | 0 .../src}/main/java/de/financer/form/NewAccountGroupForm.java | 0 .../main/java/de/financer/form/NewRecurringTransactionForm.java | 0 .../src}/main/java/de/financer/form/NewTransactionForm.java | 0 .../de/financer/form/RecurringToTransactionWithAmountForm.java | 0 .../src}/main/java/de/financer/model/Account.java | 0 .../src}/main/java/de/financer/model/AccountGroup.java | 0 .../src}/main/java/de/financer/model/AccountStatus.java | 0 .../src}/main/java/de/financer/model/AccountType.java | 0 .../src}/main/java/de/financer/model/HolidayWeekendType.java | 0 .../src}/main/java/de/financer/model/IntervalType.java | 0 .../src}/main/java/de/financer/model/RecurringTransaction.java | 0 .../src}/main/java/de/financer/model/Transaction.java | 0 .../src}/main/java/de/financer/util/ControllerUtils.java | 0 .../src}/main/java/de/financer/util/TransactionUtils.java | 0 .../de/financer/util/comparator/AccountByTypeByIdComparator.java | 0 .../util/comparator/TransactionByDateByIdDescComparator.java | 0 .../src}/main/resources/config/application-dev.properties | 0 .../src}/main/resources/config/application.properties | 0 .../src}/main/resources/i18n/message.properties | 0 .../src}/main/resources/i18n/message_de_DE.properties | 0 .../src}/main/resources/static/css/main.css | 0 .../src}/main/resources/templates/account/accountDetails.html | 0 .../src}/main/resources/templates/account/accountOverview.html | 0 .../src}/main/resources/templates/account/newAccount.html | 0 .../main/resources/templates/accountGroup/newAccountGroup.html | 0 .../src}/main/resources/templates/includes/footer.html | 0 .../templates/recurringTransaction/newRecurringTransaction.html | 0 .../recurringTransaction/recurringToTransactionWithAmount.html | 0 .../templates/recurringTransaction/recurringTransactionList.html | 0 .../src}/main/resources/templates/transaction/newTransaction.html | 0 .../src}/test/java/de/financer/FinancerApplicationBootTest.java | 0 .../src}/test/resources/application-integrationtest.properties | 0 53 files changed, 0 insertions(+), 0 deletions(-) rename .gitignore => financer-web-client/.gitignore (100%) rename pom.xml => financer-web-client/pom.xml (100%) rename {src => financer-web-client/src}/main/java/de/financer/FinancerApplication.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/ResponseReason.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/config/FinancerConfig.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/AccountController.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/AccountGroupController.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/Function.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/RecurringTransactionController.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/TransactionController.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/handler/NoExceptionResponseErrorHandler.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/template/FinancerRestTemplate.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/template/GetAccountByKeyTemplate.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/template/GetAllAccountGroupsTemplate.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/template/GetAllAccountsTemplate.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/template/GetAllActiveRecurringTransactionsTemplate.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/template/GetAllRecurringTransactionsDueTodayTemplate.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/template/GetAllRecurringTransactionsTemplate.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/template/GetAllTransactionsForAccountTemplate.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/controller/template/StringTemplate.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/form/NewAccountForm.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/form/NewAccountGroupForm.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/form/NewRecurringTransactionForm.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/form/NewTransactionForm.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/form/RecurringToTransactionWithAmountForm.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/model/Account.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/model/AccountGroup.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/model/AccountStatus.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/model/AccountType.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/model/HolidayWeekendType.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/model/IntervalType.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/model/RecurringTransaction.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/model/Transaction.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/util/ControllerUtils.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/util/TransactionUtils.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/util/comparator/AccountByTypeByIdComparator.java (100%) rename {src => financer-web-client/src}/main/java/de/financer/util/comparator/TransactionByDateByIdDescComparator.java (100%) rename {src => financer-web-client/src}/main/resources/config/application-dev.properties (100%) rename {src => financer-web-client/src}/main/resources/config/application.properties (100%) rename {src => financer-web-client/src}/main/resources/i18n/message.properties (100%) rename {src => financer-web-client/src}/main/resources/i18n/message_de_DE.properties (100%) rename {src => financer-web-client/src}/main/resources/static/css/main.css (100%) rename {src => financer-web-client/src}/main/resources/templates/account/accountDetails.html (100%) rename {src => financer-web-client/src}/main/resources/templates/account/accountOverview.html (100%) rename {src => financer-web-client/src}/main/resources/templates/account/newAccount.html (100%) rename {src => financer-web-client/src}/main/resources/templates/accountGroup/newAccountGroup.html (100%) rename {src => financer-web-client/src}/main/resources/templates/includes/footer.html (100%) rename {src => financer-web-client/src}/main/resources/templates/recurringTransaction/newRecurringTransaction.html (100%) rename {src => financer-web-client/src}/main/resources/templates/recurringTransaction/recurringToTransactionWithAmount.html (100%) rename {src => financer-web-client/src}/main/resources/templates/recurringTransaction/recurringTransactionList.html (100%) rename {src => financer-web-client/src}/main/resources/templates/transaction/newTransaction.html (100%) rename {src => financer-web-client/src}/test/java/de/financer/FinancerApplicationBootTest.java (100%) rename {src => financer-web-client/src}/test/resources/application-integrationtest.properties (100%) diff --git a/.gitignore b/financer-web-client/.gitignore similarity index 100% rename from .gitignore rename to financer-web-client/.gitignore diff --git a/pom.xml b/financer-web-client/pom.xml similarity index 100% rename from pom.xml rename to financer-web-client/pom.xml diff --git a/src/main/java/de/financer/FinancerApplication.java b/financer-web-client/src/main/java/de/financer/FinancerApplication.java similarity index 100% rename from src/main/java/de/financer/FinancerApplication.java rename to financer-web-client/src/main/java/de/financer/FinancerApplication.java diff --git a/src/main/java/de/financer/ResponseReason.java b/financer-web-client/src/main/java/de/financer/ResponseReason.java similarity index 100% rename from src/main/java/de/financer/ResponseReason.java rename to financer-web-client/src/main/java/de/financer/ResponseReason.java diff --git a/src/main/java/de/financer/config/FinancerConfig.java b/financer-web-client/src/main/java/de/financer/config/FinancerConfig.java similarity index 100% rename from src/main/java/de/financer/config/FinancerConfig.java rename to financer-web-client/src/main/java/de/financer/config/FinancerConfig.java diff --git a/src/main/java/de/financer/controller/AccountController.java b/financer-web-client/src/main/java/de/financer/controller/AccountController.java similarity index 100% rename from src/main/java/de/financer/controller/AccountController.java rename to financer-web-client/src/main/java/de/financer/controller/AccountController.java diff --git a/src/main/java/de/financer/controller/AccountGroupController.java b/financer-web-client/src/main/java/de/financer/controller/AccountGroupController.java similarity index 100% rename from src/main/java/de/financer/controller/AccountGroupController.java rename to financer-web-client/src/main/java/de/financer/controller/AccountGroupController.java diff --git a/src/main/java/de/financer/controller/Function.java b/financer-web-client/src/main/java/de/financer/controller/Function.java similarity index 100% rename from src/main/java/de/financer/controller/Function.java rename to financer-web-client/src/main/java/de/financer/controller/Function.java diff --git a/src/main/java/de/financer/controller/RecurringTransactionController.java b/financer-web-client/src/main/java/de/financer/controller/RecurringTransactionController.java similarity index 100% rename from src/main/java/de/financer/controller/RecurringTransactionController.java rename to financer-web-client/src/main/java/de/financer/controller/RecurringTransactionController.java diff --git a/src/main/java/de/financer/controller/TransactionController.java b/financer-web-client/src/main/java/de/financer/controller/TransactionController.java similarity index 100% rename from src/main/java/de/financer/controller/TransactionController.java rename to financer-web-client/src/main/java/de/financer/controller/TransactionController.java diff --git a/src/main/java/de/financer/controller/handler/NoExceptionResponseErrorHandler.java b/financer-web-client/src/main/java/de/financer/controller/handler/NoExceptionResponseErrorHandler.java similarity index 100% rename from src/main/java/de/financer/controller/handler/NoExceptionResponseErrorHandler.java rename to financer-web-client/src/main/java/de/financer/controller/handler/NoExceptionResponseErrorHandler.java diff --git a/src/main/java/de/financer/controller/template/FinancerRestTemplate.java b/financer-web-client/src/main/java/de/financer/controller/template/FinancerRestTemplate.java similarity index 100% rename from src/main/java/de/financer/controller/template/FinancerRestTemplate.java rename to financer-web-client/src/main/java/de/financer/controller/template/FinancerRestTemplate.java diff --git a/src/main/java/de/financer/controller/template/GetAccountByKeyTemplate.java b/financer-web-client/src/main/java/de/financer/controller/template/GetAccountByKeyTemplate.java similarity index 100% rename from src/main/java/de/financer/controller/template/GetAccountByKeyTemplate.java rename to financer-web-client/src/main/java/de/financer/controller/template/GetAccountByKeyTemplate.java diff --git a/src/main/java/de/financer/controller/template/GetAllAccountGroupsTemplate.java b/financer-web-client/src/main/java/de/financer/controller/template/GetAllAccountGroupsTemplate.java similarity index 100% rename from src/main/java/de/financer/controller/template/GetAllAccountGroupsTemplate.java rename to financer-web-client/src/main/java/de/financer/controller/template/GetAllAccountGroupsTemplate.java diff --git a/src/main/java/de/financer/controller/template/GetAllAccountsTemplate.java b/financer-web-client/src/main/java/de/financer/controller/template/GetAllAccountsTemplate.java similarity index 100% rename from src/main/java/de/financer/controller/template/GetAllAccountsTemplate.java rename to financer-web-client/src/main/java/de/financer/controller/template/GetAllAccountsTemplate.java diff --git a/src/main/java/de/financer/controller/template/GetAllActiveRecurringTransactionsTemplate.java b/financer-web-client/src/main/java/de/financer/controller/template/GetAllActiveRecurringTransactionsTemplate.java similarity index 100% rename from src/main/java/de/financer/controller/template/GetAllActiveRecurringTransactionsTemplate.java rename to financer-web-client/src/main/java/de/financer/controller/template/GetAllActiveRecurringTransactionsTemplate.java diff --git a/src/main/java/de/financer/controller/template/GetAllRecurringTransactionsDueTodayTemplate.java b/financer-web-client/src/main/java/de/financer/controller/template/GetAllRecurringTransactionsDueTodayTemplate.java similarity index 100% rename from src/main/java/de/financer/controller/template/GetAllRecurringTransactionsDueTodayTemplate.java rename to financer-web-client/src/main/java/de/financer/controller/template/GetAllRecurringTransactionsDueTodayTemplate.java diff --git a/src/main/java/de/financer/controller/template/GetAllRecurringTransactionsTemplate.java b/financer-web-client/src/main/java/de/financer/controller/template/GetAllRecurringTransactionsTemplate.java similarity index 100% rename from src/main/java/de/financer/controller/template/GetAllRecurringTransactionsTemplate.java rename to financer-web-client/src/main/java/de/financer/controller/template/GetAllRecurringTransactionsTemplate.java diff --git a/src/main/java/de/financer/controller/template/GetAllTransactionsForAccountTemplate.java b/financer-web-client/src/main/java/de/financer/controller/template/GetAllTransactionsForAccountTemplate.java similarity index 100% rename from src/main/java/de/financer/controller/template/GetAllTransactionsForAccountTemplate.java rename to financer-web-client/src/main/java/de/financer/controller/template/GetAllTransactionsForAccountTemplate.java diff --git a/src/main/java/de/financer/controller/template/StringTemplate.java b/financer-web-client/src/main/java/de/financer/controller/template/StringTemplate.java similarity index 100% rename from src/main/java/de/financer/controller/template/StringTemplate.java rename to financer-web-client/src/main/java/de/financer/controller/template/StringTemplate.java diff --git a/src/main/java/de/financer/form/NewAccountForm.java b/financer-web-client/src/main/java/de/financer/form/NewAccountForm.java similarity index 100% rename from src/main/java/de/financer/form/NewAccountForm.java rename to financer-web-client/src/main/java/de/financer/form/NewAccountForm.java diff --git a/src/main/java/de/financer/form/NewAccountGroupForm.java b/financer-web-client/src/main/java/de/financer/form/NewAccountGroupForm.java similarity index 100% rename from src/main/java/de/financer/form/NewAccountGroupForm.java rename to financer-web-client/src/main/java/de/financer/form/NewAccountGroupForm.java diff --git a/src/main/java/de/financer/form/NewRecurringTransactionForm.java b/financer-web-client/src/main/java/de/financer/form/NewRecurringTransactionForm.java similarity index 100% rename from src/main/java/de/financer/form/NewRecurringTransactionForm.java rename to financer-web-client/src/main/java/de/financer/form/NewRecurringTransactionForm.java diff --git a/src/main/java/de/financer/form/NewTransactionForm.java b/financer-web-client/src/main/java/de/financer/form/NewTransactionForm.java similarity index 100% rename from src/main/java/de/financer/form/NewTransactionForm.java rename to financer-web-client/src/main/java/de/financer/form/NewTransactionForm.java diff --git a/src/main/java/de/financer/form/RecurringToTransactionWithAmountForm.java b/financer-web-client/src/main/java/de/financer/form/RecurringToTransactionWithAmountForm.java similarity index 100% rename from src/main/java/de/financer/form/RecurringToTransactionWithAmountForm.java rename to financer-web-client/src/main/java/de/financer/form/RecurringToTransactionWithAmountForm.java diff --git a/src/main/java/de/financer/model/Account.java b/financer-web-client/src/main/java/de/financer/model/Account.java similarity index 100% rename from src/main/java/de/financer/model/Account.java rename to financer-web-client/src/main/java/de/financer/model/Account.java diff --git a/src/main/java/de/financer/model/AccountGroup.java b/financer-web-client/src/main/java/de/financer/model/AccountGroup.java similarity index 100% rename from src/main/java/de/financer/model/AccountGroup.java rename to financer-web-client/src/main/java/de/financer/model/AccountGroup.java diff --git a/src/main/java/de/financer/model/AccountStatus.java b/financer-web-client/src/main/java/de/financer/model/AccountStatus.java similarity index 100% rename from src/main/java/de/financer/model/AccountStatus.java rename to financer-web-client/src/main/java/de/financer/model/AccountStatus.java diff --git a/src/main/java/de/financer/model/AccountType.java b/financer-web-client/src/main/java/de/financer/model/AccountType.java similarity index 100% rename from src/main/java/de/financer/model/AccountType.java rename to financer-web-client/src/main/java/de/financer/model/AccountType.java diff --git a/src/main/java/de/financer/model/HolidayWeekendType.java b/financer-web-client/src/main/java/de/financer/model/HolidayWeekendType.java similarity index 100% rename from src/main/java/de/financer/model/HolidayWeekendType.java rename to financer-web-client/src/main/java/de/financer/model/HolidayWeekendType.java diff --git a/src/main/java/de/financer/model/IntervalType.java b/financer-web-client/src/main/java/de/financer/model/IntervalType.java similarity index 100% rename from src/main/java/de/financer/model/IntervalType.java rename to financer-web-client/src/main/java/de/financer/model/IntervalType.java diff --git a/src/main/java/de/financer/model/RecurringTransaction.java b/financer-web-client/src/main/java/de/financer/model/RecurringTransaction.java similarity index 100% rename from src/main/java/de/financer/model/RecurringTransaction.java rename to financer-web-client/src/main/java/de/financer/model/RecurringTransaction.java diff --git a/src/main/java/de/financer/model/Transaction.java b/financer-web-client/src/main/java/de/financer/model/Transaction.java similarity index 100% rename from src/main/java/de/financer/model/Transaction.java rename to financer-web-client/src/main/java/de/financer/model/Transaction.java diff --git a/src/main/java/de/financer/util/ControllerUtils.java b/financer-web-client/src/main/java/de/financer/util/ControllerUtils.java similarity index 100% rename from src/main/java/de/financer/util/ControllerUtils.java rename to financer-web-client/src/main/java/de/financer/util/ControllerUtils.java diff --git a/src/main/java/de/financer/util/TransactionUtils.java b/financer-web-client/src/main/java/de/financer/util/TransactionUtils.java similarity index 100% rename from src/main/java/de/financer/util/TransactionUtils.java rename to financer-web-client/src/main/java/de/financer/util/TransactionUtils.java diff --git a/src/main/java/de/financer/util/comparator/AccountByTypeByIdComparator.java b/financer-web-client/src/main/java/de/financer/util/comparator/AccountByTypeByIdComparator.java similarity index 100% rename from src/main/java/de/financer/util/comparator/AccountByTypeByIdComparator.java rename to financer-web-client/src/main/java/de/financer/util/comparator/AccountByTypeByIdComparator.java diff --git a/src/main/java/de/financer/util/comparator/TransactionByDateByIdDescComparator.java b/financer-web-client/src/main/java/de/financer/util/comparator/TransactionByDateByIdDescComparator.java similarity index 100% rename from src/main/java/de/financer/util/comparator/TransactionByDateByIdDescComparator.java rename to financer-web-client/src/main/java/de/financer/util/comparator/TransactionByDateByIdDescComparator.java diff --git a/src/main/resources/config/application-dev.properties b/financer-web-client/src/main/resources/config/application-dev.properties similarity index 100% rename from src/main/resources/config/application-dev.properties rename to financer-web-client/src/main/resources/config/application-dev.properties diff --git a/src/main/resources/config/application.properties b/financer-web-client/src/main/resources/config/application.properties similarity index 100% rename from src/main/resources/config/application.properties rename to financer-web-client/src/main/resources/config/application.properties diff --git a/src/main/resources/i18n/message.properties b/financer-web-client/src/main/resources/i18n/message.properties similarity index 100% rename from src/main/resources/i18n/message.properties rename to financer-web-client/src/main/resources/i18n/message.properties diff --git a/src/main/resources/i18n/message_de_DE.properties b/financer-web-client/src/main/resources/i18n/message_de_DE.properties similarity index 100% rename from src/main/resources/i18n/message_de_DE.properties rename to financer-web-client/src/main/resources/i18n/message_de_DE.properties diff --git a/src/main/resources/static/css/main.css b/financer-web-client/src/main/resources/static/css/main.css similarity index 100% rename from src/main/resources/static/css/main.css rename to financer-web-client/src/main/resources/static/css/main.css diff --git a/src/main/resources/templates/account/accountDetails.html b/financer-web-client/src/main/resources/templates/account/accountDetails.html similarity index 100% rename from src/main/resources/templates/account/accountDetails.html rename to financer-web-client/src/main/resources/templates/account/accountDetails.html diff --git a/src/main/resources/templates/account/accountOverview.html b/financer-web-client/src/main/resources/templates/account/accountOverview.html similarity index 100% rename from src/main/resources/templates/account/accountOverview.html rename to financer-web-client/src/main/resources/templates/account/accountOverview.html diff --git a/src/main/resources/templates/account/newAccount.html b/financer-web-client/src/main/resources/templates/account/newAccount.html similarity index 100% rename from src/main/resources/templates/account/newAccount.html rename to financer-web-client/src/main/resources/templates/account/newAccount.html diff --git a/src/main/resources/templates/accountGroup/newAccountGroup.html b/financer-web-client/src/main/resources/templates/accountGroup/newAccountGroup.html similarity index 100% rename from src/main/resources/templates/accountGroup/newAccountGroup.html rename to financer-web-client/src/main/resources/templates/accountGroup/newAccountGroup.html diff --git a/src/main/resources/templates/includes/footer.html b/financer-web-client/src/main/resources/templates/includes/footer.html similarity index 100% rename from src/main/resources/templates/includes/footer.html rename to financer-web-client/src/main/resources/templates/includes/footer.html diff --git a/src/main/resources/templates/recurringTransaction/newRecurringTransaction.html b/financer-web-client/src/main/resources/templates/recurringTransaction/newRecurringTransaction.html similarity index 100% rename from src/main/resources/templates/recurringTransaction/newRecurringTransaction.html rename to financer-web-client/src/main/resources/templates/recurringTransaction/newRecurringTransaction.html diff --git a/src/main/resources/templates/recurringTransaction/recurringToTransactionWithAmount.html b/financer-web-client/src/main/resources/templates/recurringTransaction/recurringToTransactionWithAmount.html similarity index 100% rename from src/main/resources/templates/recurringTransaction/recurringToTransactionWithAmount.html rename to financer-web-client/src/main/resources/templates/recurringTransaction/recurringToTransactionWithAmount.html diff --git a/src/main/resources/templates/recurringTransaction/recurringTransactionList.html b/financer-web-client/src/main/resources/templates/recurringTransaction/recurringTransactionList.html similarity index 100% rename from src/main/resources/templates/recurringTransaction/recurringTransactionList.html rename to financer-web-client/src/main/resources/templates/recurringTransaction/recurringTransactionList.html diff --git a/src/main/resources/templates/transaction/newTransaction.html b/financer-web-client/src/main/resources/templates/transaction/newTransaction.html similarity index 100% rename from src/main/resources/templates/transaction/newTransaction.html rename to financer-web-client/src/main/resources/templates/transaction/newTransaction.html diff --git a/src/test/java/de/financer/FinancerApplicationBootTest.java b/financer-web-client/src/test/java/de/financer/FinancerApplicationBootTest.java similarity index 100% rename from src/test/java/de/financer/FinancerApplicationBootTest.java rename to financer-web-client/src/test/java/de/financer/FinancerApplicationBootTest.java diff --git a/src/test/resources/application-integrationtest.properties b/financer-web-client/src/test/resources/application-integrationtest.properties similarity index 100% rename from src/test/resources/application-integrationtest.properties rename to financer-web-client/src/test/resources/application-integrationtest.properties