#25 Account edit mask
This commit is contained in:
@@ -54,6 +54,24 @@ public class AccountController {
|
||||
return responseReason.toResponseEntity();
|
||||
}
|
||||
|
||||
@RequestMapping("editAccount")
|
||||
public ResponseEntity editAccount(Long id, String key, String accountGroupName) {
|
||||
final String decoded = ControllerUtil.urlDecode(key);
|
||||
final String decodedGroup = ControllerUtil.urlDecode(accountGroupName);
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(String.format("/accounts/editAccount got parameters: %s, %s, %s", id, decoded, decodedGroup));
|
||||
}
|
||||
|
||||
final ResponseReason responseReason = this.accountService.editAccount(id, decoded, decodedGroup);
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(String.format("/accounts/editAccount returns with %s", responseReason.name()));
|
||||
}
|
||||
|
||||
return responseReason.toResponseEntity();
|
||||
}
|
||||
|
||||
@RequestMapping("closeAccount")
|
||||
public ResponseEntity closeAccount(String key) {
|
||||
final String decoded = ControllerUtil.urlDecode(key);
|
||||
|
||||
@@ -72,12 +72,12 @@ public class AccountService {
|
||||
*
|
||||
* @param key the key of the new account
|
||||
* @param type the type of the new account. Must be one of {@link AccountType}.
|
||||
* @param accountGroupName the name of the account group to use, can be <code>null</code>
|
||||
* @param accountGroupName the name of the account group to use
|
||||
*
|
||||
* @return {@link ResponseReason#INVALID_ACCOUNT_TYPE} if the given type is not a valid {@link AccountType}, {@link
|
||||
* ResponseReason#UNKNOWN_ERROR} if an unexpected error occurs, {@link ResponseReason#OK} if the operation completed
|
||||
* successfully, {@link ResponseReason#DUPLICATE_ACCOUNT_KEY} if an account with the given key already exists and
|
||||
* {@link ResponseReason#ACCOUNT_GROUP_NOT_FOUND} if the optional parameter
|
||||
* {@link ResponseReason#ACCOUNT_GROUP_NOT_FOUND} if the parameter
|
||||
* <code>accountGroupName</code> does not identify a valid account group. Never returns <code>null</code>.
|
||||
*/
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
@@ -87,8 +87,6 @@ public class AccountService {
|
||||
}
|
||||
|
||||
final Account account = new Account();
|
||||
|
||||
if (StringUtils.isNotEmpty(accountGroupName)) {
|
||||
final AccountGroup accountGroup = this.accountGroupService.getAccountGroupByName(accountGroupName);
|
||||
|
||||
if (accountGroup == null) {
|
||||
@@ -96,7 +94,6 @@ public class AccountService {
|
||||
}
|
||||
|
||||
account.setAccountGroup(accountGroup);
|
||||
}
|
||||
|
||||
account.setKey(key);
|
||||
account.setType(AccountType.valueOf(type));
|
||||
@@ -120,6 +117,51 @@ public class AccountService {
|
||||
return ResponseReason.OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method edits the account with the given id.
|
||||
*
|
||||
* @param id the id of the account to edit
|
||||
* @param key the new key of the account
|
||||
* @param accountGroupName the new name of the account group to use
|
||||
*
|
||||
* @return {@link ResponseReason#OK} if the operation completed successfully, {@link ResponseReason#UNKNOWN_ERROR}
|
||||
* if an unexpected error occurs, {@link ResponseReason#DUPLICATE_ACCOUNT_KEY} if an account with the given key
|
||||
* already exists and {@link ResponseReason#ACCOUNT_GROUP_NOT_FOUND} if the parameter
|
||||
* <code>accountGroupName</code> does not identify a valid account group or {@link ResponseReason#ACCOUNT_NOT_FOUND}
|
||||
* if the given id does not identify a valid account. Never returns <code>null</code>.
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public ResponseReason editAccount(Long id, String key, String accountGroupName) {
|
||||
final Account account = this.accountRepository.findById(id).orElse(null);
|
||||
|
||||
if(account == null) {
|
||||
return ResponseReason.ACCOUNT_NOT_FOUND;
|
||||
}
|
||||
|
||||
final AccountGroup accountGroup = this.accountGroupService.getAccountGroupByName(accountGroupName);
|
||||
|
||||
if (accountGroup == null) {
|
||||
return ResponseReason.ACCOUNT_GROUP_NOT_FOUND;
|
||||
}
|
||||
|
||||
account.setKey(key);
|
||||
account.setAccountGroup(accountGroup);
|
||||
|
||||
try {
|
||||
this.accountRepository.save(account);
|
||||
} catch (DataIntegrityViolationException dive) {
|
||||
LOGGER.error(String.format("Duplicate key! %s|%s", key, accountGroupName), dive);
|
||||
|
||||
return ResponseReason.DUPLICATE_ACCOUNT_KEY;
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(String.format("Could not save account %s|%s", key, accountGroupName), e);
|
||||
|
||||
return ResponseReason.UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
return ResponseReason.OK;
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public ResponseReason closeAccount(String key) {
|
||||
return setAccountStatus(key, AccountStatus.CLOSED);
|
||||
|
||||
@@ -3,6 +3,7 @@ package de.financer.service;
|
||||
import de.financer.ResponseReason;
|
||||
import de.financer.dba.AccountRepository;
|
||||
import de.financer.model.Account;
|
||||
import de.financer.model.AccountGroup;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -40,9 +41,11 @@ public class AccountService_createAccountTest {
|
||||
public void test_createAccount_UNKNOWN_ERROR() {
|
||||
// Arrange
|
||||
Mockito.doThrow(new NullPointerException()).when(this.accountRepository).save(Mockito.any(Account.class));
|
||||
Mockito.when(this.accountGroupService.getAccountGroupByName(Mockito.anyString()))
|
||||
.thenReturn(Mockito.mock(AccountGroup.class));
|
||||
|
||||
// Act
|
||||
ResponseReason response = this.classUnderTest.createAccount("Test", "BANK", null);
|
||||
ResponseReason response = this.classUnderTest.createAccount("Test", "BANK", "Group1");
|
||||
|
||||
// Assert
|
||||
Assert.assertEquals(ResponseReason.UNKNOWN_ERROR, response);
|
||||
@@ -51,10 +54,11 @@ public class AccountService_createAccountTest {
|
||||
@Test
|
||||
public void test_createAccount_OK() {
|
||||
// Arrange
|
||||
// Nothing to do
|
||||
Mockito.when(this.accountGroupService.getAccountGroupByName(Mockito.anyString()))
|
||||
.thenReturn(Mockito.mock(AccountGroup.class));
|
||||
|
||||
// Act
|
||||
ResponseReason response = this.classUnderTest.createAccount("Test", "BANK", null);
|
||||
ResponseReason response = this.classUnderTest.createAccount("Test", "BANK", "Group1");
|
||||
|
||||
// Assert
|
||||
Assert.assertEquals(ResponseReason.OK, response);
|
||||
@@ -79,9 +83,11 @@ public class AccountService_createAccountTest {
|
||||
public void test_createAccount_DUPLICATE_ACCOUNT_KEY() {
|
||||
// Arrange
|
||||
Mockito.doThrow(new DataIntegrityViolationException("DIVE")).when(this.accountRepository).save(Mockito.any(Account.class));
|
||||
Mockito.when(this.accountGroupService.getAccountGroupByName(Mockito.anyString()))
|
||||
.thenReturn(Mockito.mock(AccountGroup.class));
|
||||
|
||||
// Act
|
||||
ResponseReason response = this.classUnderTest.createAccount("Test", "BANK", null);
|
||||
ResponseReason response = this.classUnderTest.createAccount("Test", "BANK", "Group1");
|
||||
|
||||
// Assert
|
||||
Assert.assertEquals(ResponseReason.DUPLICATE_ACCOUNT_KEY, response);
|
||||
|
||||
@@ -5,6 +5,7 @@ import de.financer.config.FinancerConfig;
|
||||
import de.financer.decorator.AccountDecorator;
|
||||
import de.financer.dto.Order;
|
||||
import de.financer.dto.SearchTransactionsResponseDto;
|
||||
import de.financer.form.EditAccountForm;
|
||||
import de.financer.form.NewAccountForm;
|
||||
import de.financer.model.*;
|
||||
import de.financer.notification.Notification;
|
||||
@@ -17,6 +18,7 @@ import de.financer.util.TransactionUtils;
|
||||
import org.apache.commons.collections4.IterableUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jfree.data.resources.DataPackageResources_es;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
@@ -30,6 +32,7 @@ import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Controller
|
||||
@@ -87,7 +90,7 @@ public class AccountController {
|
||||
}
|
||||
|
||||
@PostMapping("/saveAccount")
|
||||
public String saveAccont(NewAccountForm form, Model model) {
|
||||
public String saveAccount(NewAccountForm form, Model model) {
|
||||
final UriComponentsBuilder builder = UriComponentsBuilder
|
||||
.fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.ACC_CREATE_ACCOUNT))
|
||||
.queryParam("key", form.getKey())
|
||||
@@ -225,6 +228,56 @@ public class AccountController {
|
||||
return "redirect:" + navigateTo;
|
||||
}
|
||||
|
||||
@GetMapping("/editAccount")
|
||||
public String editAccount(Model model, String key) {
|
||||
_editAccount(model, key, Optional.empty(), Optional.empty());
|
||||
|
||||
return "account/editAccount";
|
||||
}
|
||||
|
||||
private void _editAccount(Model model, String originalKey, Optional<String> newKey, Optional<String> newGroup) {
|
||||
final ResponseEntity<Account> exchange = new GetAccountByKeyTemplate().exchange(this.financerConfig, originalKey);
|
||||
final ResponseEntity<Iterable<AccountGroup>> accountGroupResponse = new GetAllAccountGroupsTemplate()
|
||||
.exchange(this.financerConfig);
|
||||
final Account account = exchange.getBody();
|
||||
|
||||
model.addAttribute("accountGroups", ControllerUtils.sortAccountGroups(accountGroupResponse.getBody()));
|
||||
|
||||
final EditAccountForm form = new EditAccountForm();
|
||||
|
||||
form.setKey(newKey.orElse(account.getKey()));
|
||||
form.setGroup(newGroup.orElse(Optional.ofNullable(account.getAccountGroup()).map(AccountGroup::getName).orElse(null)));
|
||||
form.setId(account.getId().toString());
|
||||
form.setOriginalKey(originalKey);
|
||||
|
||||
model.addAttribute("form", form);
|
||||
|
||||
ControllerUtils.addVersionAttribute(model, this.financerConfig);
|
||||
ControllerUtils.addCurrencySymbol(model, this.financerConfig);
|
||||
ControllerUtils.addDarkMode(model, this.financerConfig);
|
||||
}
|
||||
|
||||
@PostMapping("/editAccount")
|
||||
public String editAccount(Model model, EditAccountForm form) {
|
||||
final UriComponentsBuilder editBuilder = UriComponentsBuilder
|
||||
.fromHttpUrl(ControllerUtils.buildUrl(this.financerConfig, Function.ACC_EDIT_ACCOUNT))
|
||||
.queryParam("id", form.getId())
|
||||
.queryParam("key", form.getKey())
|
||||
.queryParam("accountGroupName", form.getGroup());
|
||||
|
||||
final ResponseEntity<String> closeResponse = new StringTemplate().exchange(editBuilder);
|
||||
final ResponseReason responseReason = ResponseReason.fromResponseEntity(closeResponse);
|
||||
|
||||
if (!ResponseReason.OK.equals(responseReason)) {
|
||||
_editAccount(model, form.getOriginalKey(), Optional.of(form.getKey()), Optional.of(form.getGroup()));
|
||||
|
||||
return "account/editAccount";
|
||||
}
|
||||
|
||||
|
||||
return "redirect:/accountOverview";
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -9,6 +9,7 @@ public enum Function {
|
||||
ACC_CURRENT_ASSETS("accounts/getCurrentAssets"),
|
||||
ACC_GET_ACC_EXPENSES("accounts/getAccountExpenses"),
|
||||
ACC_GET_ACC_EXPENSES_CURRENT_EXPENSE_PERIOD("accounts/getAccountExpensesCurrentExpensePeriod"),
|
||||
ACC_EDIT_ACCOUNT("accounts/editAccount"),
|
||||
|
||||
ACC_GP_CREATE_ACCOUNT_GROUP("accountGroups/createAccountGroup"),
|
||||
ACC_GP_GET_ALL("accountGroups/getAll"),
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package de.financer.form;
|
||||
|
||||
public class EditAccountForm {
|
||||
private String key;
|
||||
private String group;
|
||||
private String id;
|
||||
private String originalKey;
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public void setGroup(String group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getOriginalKey() {
|
||||
return originalKey;
|
||||
}
|
||||
|
||||
public void setOriginalKey(String originalKey) {
|
||||
this.originalKey = originalKey;
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,11 @@ financer.account-new.label.type=Type\:
|
||||
financer.account-new.label.group=Group\:
|
||||
financer.account-new.submit=Create account
|
||||
|
||||
financer.account-edit.title=financer\: edit account
|
||||
financer.account-edit.label.key=Key\:
|
||||
financer.account-edit.label.group=Group\:
|
||||
financer.account-edit.submit=Edit 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
|
||||
@@ -104,6 +109,7 @@ financer.account-details.available-actions.close-account=Close account
|
||||
financer.account-details.available-actions.open-account=Open account
|
||||
financer.account-details.available-actions.back-to-overview=Back to overview
|
||||
financer.account-details.available-actions.create-transaction=Create new transaction
|
||||
financer.account-details.available-actions.edit-account=Edit account
|
||||
financer.transaction-list.table-header.id=ID
|
||||
financer.transaction-list.table-header.fromAccount=From account
|
||||
financer.transaction-list.table-header.toAccount=To account
|
||||
@@ -262,6 +268,7 @@ financer.heading.recurring-transaction-calendar=financer\: recurring transaction
|
||||
financer.heading.period-overview=financer\: period overview
|
||||
financer.heading.upload-transactions=financer\: upload transactions
|
||||
financer.heading.create-upload-transactions=financer\: create uploaded transactions
|
||||
financer.heading.account-edit=financer\: edit account
|
||||
|
||||
financer.cancel-back-to-overview=Cancel and back to overview
|
||||
financer.back-to-overview=Back to overview
|
||||
|
||||
@@ -40,6 +40,11 @@ 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.account-edit.title=financer\: Bearbeite Konto
|
||||
financer.account-edit.label.key=Schl\u00FCssel\:
|
||||
financer.account-edit.label.group=Gruppe\:
|
||||
financer.account-edit.submit=Konto bearbeiten
|
||||
|
||||
financer.transaction-new.title=financer\: Neue Buchung erstellen
|
||||
financer.transaction-new.label.from-account=Von Konto\:
|
||||
financer.transaction-new.label.to-account=An Konto\:
|
||||
@@ -104,6 +109,7 @@ financer.account-details.available-actions.close-account=Konto schlie\u00DFen
|
||||
financer.account-details.available-actions.open-account=Konto \u00F6ffnen
|
||||
financer.account-details.available-actions.back-to-overview=Zur\u00FCck zur \u00DCbersicht
|
||||
financer.account-details.available-actions.create-transaction=Neue Buchung erstellen
|
||||
financer.account-details.available-actions.edit-account=Konto bearbeiten
|
||||
financer.transaction-list.table-header.id=ID
|
||||
financer.transaction-list.table-header.fromAccount=Von Konto
|
||||
financer.transaction-list.table-header.toAccount=An Konto
|
||||
@@ -261,6 +267,7 @@ financer.heading.recurring-transaction-calendar=financer\: Kalender wiederkehren
|
||||
financer.heading.period-overview=financer\: Perioden\u00FCbersicht
|
||||
financer.heading.upload-transactions=financer\: Buchungen hochladen
|
||||
financer.heading.create-upload-transactions=financer\: Erstelle hochgeladene Buchungen
|
||||
financer.heading.account-edit=financer\: Bearbeite Konto
|
||||
|
||||
financer.cancel-back-to-overview=Abbrechen und zur\u00FCck zur \u00DCbersicht
|
||||
financer.back-to-overview=Zur\u00FCck zur \u00DCbersicht
|
||||
|
||||
@@ -2,9 +2,10 @@ v48 -> v49:
|
||||
- #27 The recurring transaction selection during transaction import now contains all recurring transaction instead of
|
||||
only the active ones
|
||||
- #11 It is now possible to specify a date during creation of a transaction from a recurring transaction
|
||||
- #25 Added the possibility to edit accounts
|
||||
|
||||
v47 -> v48:
|
||||
- Added new property 'transaction type' to a transaction, denoting the type of the transaction, e.g. asset swap,
|
||||
- #20 Added new property 'transaction type' to a transaction, denoting the type of the transaction, e.g. asset swap,
|
||||
expense, liability or income. This can also be queried via FQL
|
||||
- #22 Added new feature to upload a file that contains transactions, e.g. a file export from online banking. Currently
|
||||
supported is the MT940_CSV format
|
||||
|
||||
@@ -175,7 +175,8 @@ tr:hover {
|
||||
#chart-config-account-group-expenses-for-period-form *,
|
||||
#chart-config-account-expenses-for-period-form *,
|
||||
#search-transactions-form *,
|
||||
#upload-transactions-form * {
|
||||
#upload-transactions-form *,
|
||||
#edit-account-form * {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
width: 20em;
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
</div>
|
||||
<div id="account-details-action-container">
|
||||
<span th:text="#{financer.account-details.available-actions}"/>
|
||||
<a th:if="${!isClosed}" th:href="@{/editAccount(key=${account.key})}"
|
||||
th:text="#{financer.account-details.available-actions.edit-account}"/>
|
||||
<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})}"
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<title th:text="#{financer.account-edit.title}"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link th:if="${darkMode}" rel="stylesheet" th:href="@{/css/darkModeColors.css}" />
|
||||
<link th:if="${!darkMode}" rel="stylesheet" th:href="@{/css/lightModeColors.css}" />
|
||||
<link rel="stylesheet" th:href="@{/css/main.css}">
|
||||
<link rel="shortcut icon" th:href="@{/favicon.ico}" />
|
||||
</head>
|
||||
<body>
|
||||
<h1 th:text="#{financer.heading.account-edit}" />
|
||||
<span class="errorMessage" th:if="${errorMessage != null}" th:text="#{'financer.error-message.' + ${errorMessage}}"/>
|
||||
<a th:href="@{/accountOverview}" th:text="#{financer.cancel-back-to-overview}"/>
|
||||
<form id="edit-account-form" action="#" th:action="@{/editAccount}" th:object="${form}" method="post">
|
||||
<label for="inputKey" th:text="#{financer.account-edit.label.key}"/>
|
||||
<input type="text" id="inputKey" th:field="*{key}" />
|
||||
<label for="selectGroup" th:text="#{financer.account-edit.label.group}"/>
|
||||
<select id="selectGroup" th:field="*{group}">
|
||||
<option th:each="group : ${accountGroups}" th:value="${group.name}" th:text="${group.name}"/>
|
||||
</select>
|
||||
<input type="hidden" id="inputId" th:field="*{id}"/>
|
||||
<input type="hidden" id="originalKey" th:field="*{originalKey}"/>
|
||||
<input type="submit" th:value="#{financer.account-edit.submit}" />
|
||||
</form>
|
||||
<div th:replace="includes/footer :: footer"/>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user