diff --git a/financer-server/src/main/java/de/financer/controller/AccountController.java b/financer-server/src/main/java/de/financer/controller/AccountController.java index 5acd8d8..1696927 100644 --- a/financer-server/src/main/java/de/financer/controller/AccountController.java +++ b/financer-server/src/main/java/de/financer/controller/AccountController.java @@ -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); diff --git a/financer-server/src/main/java/de/financer/service/AccountService.java b/financer-server/src/main/java/de/financer/service/AccountService.java index 27122b2..8a426af 100644 --- a/financer-server/src/main/java/de/financer/service/AccountService.java +++ b/financer-server/src/main/java/de/financer/service/AccountService.java @@ -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 null + * @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 * accountGroupName does not identify a valid account group. Never returns null. */ @Transactional(propagation = Propagation.SUPPORTS) @@ -87,17 +87,14 @@ public class AccountService { } final Account account = new Account(); + final AccountGroup accountGroup = this.accountGroupService.getAccountGroupByName(accountGroupName); - if (StringUtils.isNotEmpty(accountGroupName)) { - final AccountGroup accountGroup = this.accountGroupService.getAccountGroupByName(accountGroupName); - - if (accountGroup == null) { - return ResponseReason.ACCOUNT_GROUP_NOT_FOUND; // early return - } - - account.setAccountGroup(accountGroup); + if (accountGroup == null) { + return ResponseReason.ACCOUNT_GROUP_NOT_FOUND; // early return } + account.setAccountGroup(accountGroup); + account.setKey(key); account.setType(AccountType.valueOf(type)); // If we create an account it's implicitly open @@ -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 + * accountGroupName 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 null. + */ + @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); diff --git a/financer-server/src/test/java/de/financer/service/AccountService_createAccountTest.java b/financer-server/src/test/java/de/financer/service/AccountService_createAccountTest.java index 5d5d965..a485a65 100644 --- a/financer-server/src/test/java/de/financer/service/AccountService_createAccountTest.java +++ b/financer-server/src/test/java/de/financer/service/AccountService_createAccountTest.java @@ -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); diff --git a/financer-web-client/src/main/java/de/financer/controller/AccountController.java b/financer-web-client/src/main/java/de/financer/controller/AccountController.java index 97bb17c..b9ae470 100644 --- a/financer-web-client/src/main/java/de/financer/controller/AccountController.java +++ b/financer-web-client/src/main/java/de/financer/controller/AccountController.java @@ -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 newKey, Optional newGroup) { + final ResponseEntity exchange = new GetAccountByKeyTemplate().exchange(this.financerConfig, originalKey); + final ResponseEntity> 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 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 diff --git a/financer-web-client/src/main/java/de/financer/controller/Function.java b/financer-web-client/src/main/java/de/financer/controller/Function.java index 3ad98c2..3f9ab44 100644 --- a/financer-web-client/src/main/java/de/financer/controller/Function.java +++ b/financer-web-client/src/main/java/de/financer/controller/Function.java @@ -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"), diff --git a/financer-web-client/src/main/java/de/financer/form/EditAccountForm.java b/financer-web-client/src/main/java/de/financer/form/EditAccountForm.java new file mode 100644 index 0000000..035408a --- /dev/null +++ b/financer-web-client/src/main/java/de/financer/form/EditAccountForm.java @@ -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; + } +} diff --git a/financer-web-client/src/main/resources/i18n/message.properties b/financer-web-client/src/main/resources/i18n/message.properties index 9226e9a..8ad5566 100644 --- a/financer-web-client/src/main/resources/i18n/message.properties +++ b/financer-web-client/src/main/resources/i18n/message.properties @@ -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 diff --git a/financer-web-client/src/main/resources/i18n/message_de_DE.properties b/financer-web-client/src/main/resources/i18n/message_de_DE.properties index c1e3b4e..58fe6c4 100644 --- a/financer-web-client/src/main/resources/i18n/message_de_DE.properties +++ b/financer-web-client/src/main/resources/i18n/message_de_DE.properties @@ -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 diff --git a/financer-web-client/src/main/resources/static/changelog.txt b/financer-web-client/src/main/resources/static/changelog.txt index bb187b0..a35a83a 100644 --- a/financer-web-client/src/main/resources/static/changelog.txt +++ b/financer-web-client/src/main/resources/static/changelog.txt @@ -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 diff --git a/financer-web-client/src/main/resources/static/css/main.css b/financer-web-client/src/main/resources/static/css/main.css index 2b932dd..aceecf0 100644 --- a/financer-web-client/src/main/resources/static/css/main.css +++ b/financer-web-client/src/main/resources/static/css/main.css @@ -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; diff --git a/financer-web-client/src/main/resources/templates/account/accountDetails.html b/financer-web-client/src/main/resources/templates/account/accountDetails.html index bbedff2..0fa945d 100644 --- a/financer-web-client/src/main/resources/templates/account/accountDetails.html +++ b/financer-web-client/src/main/resources/templates/account/accountDetails.html @@ -28,6 +28,8 @@