Move content to subfolder to prepare for merge with -parent repository

This commit is contained in:
2019-06-20 14:38:20 +02:00
parent 0d16aa7c0a
commit 7a8865bdf1
53 changed files with 0 additions and 0 deletions

View File

@@ -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);
}
}

View File

@@ -0,0 +1,55 @@
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),
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),
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;
ResponseReason(HttpStatus httpStatus) {
this.httpStatus = httpStatus;
}
public ResponseEntity toResponseEntity() {
return new ResponseEntity<>(this.name(), this.httpStatus);
}
public static ResponseReason fromResponseEntity(ResponseEntity<String> entity) {
for (ResponseReason reason : values()) {
if (reason.name().equals(entity.getBody())) {
return reason;
}
}
return UNKNOWN_ERROR;
}
}

View File

@@ -0,0 +1,40 @@
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;
private String version;
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;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}

View File

@@ -0,0 +1,171 @@
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 de.financer.util.TransactionUtils;
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;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.List;
@Controller
public class AccountController {
@Autowired
private FinancerConfig financerConfig;
@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()
.exchange(this.financerConfig);
final ResponseEntity<Iterable<RecurringTransaction>> 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);
ControllerUtils.addVersionAttribute(model, this.financerConfig);
return "account/accountOverview";
}
@GetMapping("/newAccount")
public String newAccount(Model model) {
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);
return "account/newAccount";
}
@PostMapping("/saveAccount")
public String saveAccont(NewAccountForm form, Model model) {
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("accountGroups", ControllerUtils.sortAccountGroups(accountGroupResponse.getBody()));
model.addAttribute("accountTypes", AccountType.valueList());
model.addAttribute("errorMessage", responseReason.name());
ControllerUtils.addVersionAttribute(model, this.financerConfig);
return "account/newAccount";
}
return "redirect:/accountOverview";
}
@GetMapping("/accountDetails")
public String accountDetails(String key, Model model) {
final ResponseEntity<Account> response = new GetAccountByKeyTemplate().exchange(this.financerConfig, key);
final Account account = response.getBody();
final ResponseEntity<Iterable<Transaction>> transactionResponse = new GetAllTransactionsForAccountTemplate()
.exchange(this.financerConfig, account.getKey());
List<Transaction> transactions = IterableUtils.toList(transactionResponse.getBody());
transactions.sort(new TransactionByDateByIdDescComparator());
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);
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<String> closeResponse = new StringTemplate().exchange(closeBuilder);
final ResponseReason responseReason = ResponseReason.fromResponseEntity(closeResponse);
if (!ResponseReason.OK.equals(responseReason)) {
final ResponseEntity<Account> response = new GetAccountByKeyTemplate().exchange(this.financerConfig, key);
final Account account = response.getBody();
final ResponseEntity<Iterable<Transaction>> transactionResponse = new GetAllTransactionsForAccountTemplate()
.exchange(this.financerConfig, account.getKey());
List<Transaction> transactions = IterableUtils.toList(transactionResponse.getBody());
transactions.sort(new TransactionByDateByIdDescComparator());
transactions.stream().forEach((t) -> TransactionUtils.adjustAmount(t, account));
model.addAttribute("account", account);
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";
}
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<String> closeResponse = new StringTemplate().exchange(openBuilder);
final ResponseReason responseReason = ResponseReason.fromResponseEntity(closeResponse);
if (!ResponseReason.OK.equals(responseReason)) {
final ResponseEntity<Account> response = new GetAccountByKeyTemplate().exchange(this.financerConfig, key);
final Account account = response.getBody();
final ResponseEntity<Iterable<Transaction>> transactionResponse = new GetAllTransactionsForAccountTemplate()
.exchange(this.financerConfig, account.getKey());
List<Transaction> transactions = IterableUtils.toList(transactionResponse.getBody());
transactions.sort(new TransactionByDateByIdDescComparator());
transactions.stream().forEach((t) -> TransactionUtils.adjustAmount(t, account));
model.addAttribute("account", account);
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";
}
return "redirect:/accountOverview";
}
}

View File

@@ -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";
}
}

View File

@@ -0,0 +1,35 @@
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"),
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"),
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;
}
}

View File

@@ -0,0 +1,237 @@
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.form.RecurringToTransactionWithAmountForm;
import de.financer.model.Account;
import de.financer.model.HolidayWeekendType;
import de.financer.model.IntervalType;
import de.financer.model.RecurringTransaction;
import de.financer.util.ControllerUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.ArrayList;
@Controller
public class RecurringTransactionController {
@Autowired
private FinancerConfig financerConfig;
@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", form);
ControllerUtils.addVersionAttribute(model, this.financerConfig);
return "recurringTransaction/newRecurringTransaction";
}
@PostMapping("/saveRecurringTransaction")
public String saveRecurringTransaction(NewRecurringTransactionForm form, Model model) {
final UriComponentsBuilder builder = UriComponentsBuilder
.fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.RT_CREATE_RECURRING_TRANSACTION))
.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())
.queryParam("remind", form.getRemind());
final ResponseEntity<String> response = new StringTemplate().exchange(builder);
final ResponseReason responseReason = ResponseReason.fromResponseEntity(response);
if (!ResponseReason.OK.equals(responseReason)) {
final ResponseEntity<Iterable<Account>> 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", form);
model.addAttribute("errorMessage", responseReason.name());
ControllerUtils.addVersionAttribute(model, this.financerConfig);
return "recurringTransaction/newRecurringTransaction";
}
return "redirect:/accountOverview";
}
@GetMapping("/recurringTransactionDueToday")
public String recurringTransactionDueToday(Model model) {
final ResponseEntity<Iterable<RecurringTransaction>> response = new GetAllRecurringTransactionsDueTodayTemplate()
.exchange(this.financerConfig);
model.addAttribute("recurringTransactions", response.getBody());
model.addAttribute("subTitle", "dueToday");
ControllerUtils.addVersionAttribute(model, this.financerConfig);
return "recurringTransaction/recurringTransactionList";
}
@GetMapping("/recurringTransactionActive")
public String recurringTransactionActive(Model model) {
final ResponseEntity<Iterable<RecurringTransaction>> response = new GetAllActiveRecurringTransactionsTemplate()
.exchange(this.financerConfig);
model.addAttribute("recurringTransactions", response.getBody());
model.addAttribute("subTitle", "active");
ControllerUtils.addVersionAttribute(model, this.financerConfig);
return "recurringTransaction/recurringTransactionList";
}
@GetMapping("/recurringTransactionAll")
public String recurringTransactionAll(Model model) {
final ResponseEntity<Iterable<RecurringTransaction>> response = new GetAllRecurringTransactionsTemplate()
.exchange(this.financerConfig);
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";
}
}

View File

@@ -0,0 +1,103 @@
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.TransactionByDateByIdDescComparator;
import org.apache.commons.collections4.IterableUtils;
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;
import java.util.List;
@Controller
public class TransactionController {
@Autowired
private FinancerConfig financerConfig;
@GetMapping("/newTransaction")
public String newTransaction(Model model) {
final ResponseEntity<Iterable<Account>> response = new GetAllAccountsTemplate().exchange(this.financerConfig);
model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(response.getBody()));
model.addAttribute("form", new NewTransactionForm());
ControllerUtils.addVersionAttribute(model, this.financerConfig);
return "transaction/newTransaction";
}
@PostMapping("/saveTransaction")
public String saveTransaction(NewTransactionForm form, Model model) {
final UriComponentsBuilder builder = UriComponentsBuilder
.fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.TR_CREATE_TRANSACTION))
.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);
if (!ResponseReason.OK.equals(responseReason)) {
final ResponseEntity<Iterable<Account>> accRes = new GetAllAccountsTemplate().exchange(this.financerConfig);
model.addAttribute("accounts", ControllerUtils.filterAndSortAccounts(accRes.getBody()));
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 TransactionByDateByIdDescComparator());
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";
}
}

View File

@@ -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 {
}
}

View File

@@ -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<T> {
public ResponseEntity<T> exchange(String url, ParameterizedTypeReference<T> type) {
final RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new NoExceptionResponseErrorHandler());
return restTemplate.exchange(url, HttpMethod.GET, null, type);
}
public ResponseEntity<T> exchange(String url, Class<T> 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;
}
}

View File

@@ -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<Account> exchange(FinancerConfig financerConfig, String key) {
final UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(ControllerUtils
.buildUrl(financerConfig, Function.ACC_GET_BY_KEY))
.queryParam("key", key);
return new FinancerRestTemplate<Account>()
.exchange(builder.toUriString(), new ParameterizedTypeReference<Account>() {
});
}
}

View File

@@ -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>>() {
});
}
}

View File

@@ -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<Iterable<Account>> exchange(FinancerConfig financerConfig) {
return new FinancerRestTemplate<Iterable<Account>>().exchange(ControllerUtils
.buildUrl(financerConfig, Function.ACC_GET_ALL), new ParameterizedTypeReference<Iterable<Account>>() {
});
}
}

View File

@@ -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<Iterable<RecurringTransaction>> exchange(FinancerConfig financerConfig) {
return new FinancerRestTemplate<Iterable<RecurringTransaction>>().exchange(ControllerUtils
.buildUrl(financerConfig, Function.RT_GET_ALL_ACTIVE), new ParameterizedTypeReference<Iterable<RecurringTransaction>>() {
});
}
}

View File

@@ -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<Iterable<RecurringTransaction>> exchange(FinancerConfig financerConfig) {
return new FinancerRestTemplate<Iterable<RecurringTransaction>>().exchange(ControllerUtils
.buildUrl(financerConfig, Function.RT_GET_ALL_DUE_TODAY), new ParameterizedTypeReference<Iterable<RecurringTransaction>>() {
});
}
}

View File

@@ -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<Iterable<RecurringTransaction>> exchange(FinancerConfig financerConfig) {
return new FinancerRestTemplate<Iterable<RecurringTransaction>>().exchange(ControllerUtils
.buildUrl(financerConfig, Function.RT_GET_ALL), new ParameterizedTypeReference<Iterable<RecurringTransaction>>() {
});
}
}

View File

@@ -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<Iterable<Transaction>> 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<Iterable<Transaction>>()
.exchange(transactionBuilder.toUriString(), new ParameterizedTypeReference<Iterable<Transaction>>() {
});
}
}

View File

@@ -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<String> exchange(UriComponentsBuilder builder) {
return new FinancerRestTemplate<String>()
.exchange(builder.toUriString(), new ParameterizedTypeReference<String>() {
});
}
}

View File

@@ -0,0 +1,31 @@
package de.financer.form;
public class NewAccountForm {
private String key;
private String type;
private String group;
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;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,85 @@
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;
private Boolean remind;
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;
}
public Boolean getRemind() {
return remind;
}
public void setRemind(Boolean remind) {
this.remind = remind;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,54 @@
package de.financer.model;
public class Account {
private Long id;
private String key;
private AccountType type;
private AccountStatus status;
private Long currentBalance;
private AccountGroup accountGroup;
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;
}
public AccountGroup getAccountGroup() {
return accountGroup;
}
public void setAccountGroup(AccountGroup accountGroup) {
this.accountGroup = accountGroup;
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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<String> valueList() {
return Arrays.stream(AccountType.values()).map(AccountType::name).collect(Collectors.toList());
}
}

View File

@@ -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,
/**
* <p>
* Indicates that the action should be deferred to the next workday.
* </p>
* <pre>
* Example 1:
* MO TU WE TH FR SA SO
* H WE WE -&gt; Holiday/WeekEnd
* X -&gt; Due date of action
* X' -&gt; Deferred, effective due date of action
* </pre>
* <pre>
* Example 2:
* TU WE TH FR SA SO MO
* H WE WE -&gt; Holiday/WeekEnd
* X -&gt; Due date of action
* X' -&gt; Deferred, effective due date of action
* </pre>
*
*/
NEXT_WORKDAY,
/**
* <p>
* Indicates that the action should preponed to the previous day
* </p>
* <pre>
* Example 1:
* MO TU WE TH FR SA SO
* H WE WE -&gt; Holiday/WeekEnd
* X -&gt; Due date of action
* X' -&gt; Earlier, effective due date of action
* </pre>
* <pre>
* Example 2:
* MO TU WE TH FR SA SO
* H WE WE -&gt; Holiday/WeekEnd
* X -&gt; Due date of action
* X' -&gt; Earlier, effective due date of action
* </pre>
*/
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<String> valueList() {
return Arrays.stream(HolidayWeekendType.values()).map(HolidayWeekendType::name).collect(Collectors.toList());
}
}

View File

@@ -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<String> valueList() {
return Arrays.stream(IntervalType.values()).map(IntervalType::name).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,92 @@
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;
private boolean remind;
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;
}
public boolean isRemind() {
return remind;
}
public void setRemind(boolean remind) {
this.remind = remind;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,63 @@
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;
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;
import java.util.Comparator;
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<Account> filterAndSortAccounts(Iterable<Account> accounts) {
return filterAndSortAccounts(accounts, false);
}
public static List<Account> filterAndSortAccounts(Iterable<Account> accounts, boolean showClosed) {
return IterableUtils.toList(accounts).stream()
.filter((acc) -> AccountStatus.OPEN
.equals(acc.getStatus()) || showClosed)
.sorted(new AccountByTypeByIdComparator())
.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;
}
// 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<RecurringTransaction> filterEmptyEntries(Iterable<RecurringTransaction> recurringTransactions) {
return IterableUtils.toList(recurringTransactions).stream()
.filter((rt) -> rt.getFromAccount() != null && rt.getToAccount() != null)
.collect(Collectors.toList());
}
public static void addVersionAttribute(Model model, FinancerConfig financerConfig) {
model.addAttribute("financerVersion", financerConfig.getVersion());
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,25 @@
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 AccountByTypeByIdComparator implements Comparator<Account> {
//@formatter:off
private static final Map<AccountType, Integer> 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) {
int typeOrder = SORT_ORDER.get(o1.getType()).compareTo(SORT_ORDER.get(o2.getType()));
return typeOrder != 0 ? typeOrder : o1.getId().compareTo(o2.getId());
}
}

View File

@@ -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;
}
}