diff --git a/src/main/java/com/andrewlalis/perfin/control/EditAccountController.java b/src/main/java/com/andrewlalis/perfin/control/EditAccountController.java index d0761f7..caf9a7b 100644 --- a/src/main/java/com/andrewlalis/perfin/control/EditAccountController.java +++ b/src/main/java/com/andrewlalis/perfin/control/EditAccountController.java @@ -1,8 +1,10 @@ package com.andrewlalis.perfin.control; import com.andrewlalis.javafx_scene_router.RouteSelectionListener; +import com.andrewlalis.perfin.data.util.CurrencyUtil; import com.andrewlalis.perfin.model.Account; import com.andrewlalis.perfin.model.AccountType; +import com.andrewlalis.perfin.model.MoneyValue; import com.andrewlalis.perfin.model.Profile; import com.andrewlalis.perfin.view.component.PropertiesPane; import com.andrewlalis.perfin.view.component.validation.ValidationApplier; @@ -52,20 +54,20 @@ public class EditAccountController implements RouteSelectionListener { public void initialize() { var nameValid = new ValidationApplier<>(new PredicateValidator() .addTerminalPredicate(s -> s != null && !s.isBlank(), "Name should not be empty.") - .addPredicate(s -> s.length() <= 63, "Name is too long.") + .addPredicate(s -> s.strip().length() <= 63, "Name is too long.") ).attachToTextField(accountNameField); var numberValid = new ValidationApplier<>(new PredicateValidator() .addTerminalPredicate(s -> s != null && !s.isBlank(), "Account number should not be empty.") - .addPredicate(s -> s.length() <= 255, "Account number is too long.") + .addPredicate(s -> s.strip().length() <= 255, "Account number is too long.") ).attachToTextField(accountNumberField); var balanceValid = new ValidationApplier<>( - new CurrencyAmountValidator(() -> accountCurrencyComboBox.getValue(), false, false) + new CurrencyAmountValidator(() -> accountCurrencyComboBox.getValue(), true, false) ).attachToTextField(initialBalanceField, accountCurrencyComboBox.valueProperty()); // Combine validity of all fields for an expression that determines if the whole form is valid. - BooleanExpression formValid = nameValid.and(numberValid).and(balanceValid); + BooleanExpression formValid = nameValid.and(numberValid).and(balanceValid.or(creatingNewAccount.not())); saveButton.disableProperty().bind(formValid.not()); List priorityCurrencies = Stream.of("USD", "EUR", "GBP", "CAD", "AUD") @@ -104,34 +106,35 @@ public class EditAccountController implements RouteSelectionListener { @FXML public void save() { + String name = accountNameField.getText().strip(); + String number = accountNumberField.getText().strip(); + AccountType type = accountTypeChoiceBox.getValue(); + Currency currency = accountCurrencyComboBox.getValue(); try ( var accountRepo = Profile.getCurrent().dataSource().getAccountRepository(); var balanceRepo = Profile.getCurrent().dataSource().getBalanceRecordRepository() ) { if (creatingNewAccount.get()) { - String name = accountNameField.getText().strip(); - String number = accountNumberField.getText().strip(); - AccountType type = accountTypeChoiceBox.getValue(); - Currency currency = accountCurrencyComboBox.getValue(); BigDecimal initialBalance = new BigDecimal(initialBalanceField.getText().strip()); List attachments = Collections.emptyList(); - boolean success = Popups.confirm(accountNameField, "Are you sure you want to create this account?"); + String prompt = String.format( + "Are you sure you want to create this account?\nName: %s\nNumber: %s\nType: %s\nInitial Balance: %s", + name, + number, + type.toString(), + CurrencyUtil.formatMoneyWithCurrencyPrefix(new MoneyValue(initialBalance, currency)) + ); + boolean success = Popups.confirm(accountNameField, prompt); if (success) { long id = accountRepo.insert(type, number, name, currency); balanceRepo.insert(LocalDateTime.now(ZoneOffset.UTC), id, initialBalance, currency, attachments); - // Once we create the new account, go to the account. Account newAccount = accountRepo.findById(id).orElseThrow(); router.replace("account", newAccount); } } else { - log.debug("Updating account {}", account.id); - account.setName(accountNameField.getText().strip()); - account.setAccountNumber(accountNumberField.getText().strip()); - account.setType(accountTypeChoiceBox.getValue()); - account.setCurrency(accountCurrencyComboBox.getValue()); - accountRepo.update(account); + accountRepo.update(account.id, type, number, name, currency); Account updatedAccount = accountRepo.findById(account.id).orElseThrow(); router.replace("account", updatedAccount); } diff --git a/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java b/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java index 0b59adb..e97bee6 100644 --- a/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java +++ b/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java @@ -23,8 +23,7 @@ public interface AccountRepository extends Repository, AutoCloseable { List findTopNRecentlyActive(int n, int daysSinceLastActive); List findAllByCurrency(Currency currency); Optional findById(long id); - void updateName(long id, String name); - void update(Account account); + void update(long accountId, AccountType type, String accountNumber, String name, Currency currency); void delete(Account account); void archive(long accountId); void unarchive(long accountId); diff --git a/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java b/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java index 6752f7b..04a8594 100644 --- a/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java +++ b/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java @@ -113,11 +113,6 @@ public record JdbcAccountRepository(Connection conn, Path contentDir) implements return DbUtil.findById(conn, "SELECT * FROM account WHERE id = ?", id, JdbcAccountRepository::parseAccount); } - @Override - public void updateName(long id, String name) { - DbUtil.updateOne(conn, "UPDATE account SET name = ? WHERE id = ? AND NOT archived", List.of(name, id)); - } - @Override public BigDecimal deriveBalance(long accountId, Instant timestamp) { // First find the account itself, since its properties influence the balance. @@ -215,18 +210,33 @@ public record JdbcAccountRepository(Connection conn, Path contentDir) implements } @Override - public void update(Account account) { - DbUtil.updateOne( - conn, - "UPDATE account SET name = ?, account_number = ?, currency = ?, account_type = ? WHERE id = ?", - List.of( - account.getName(), - account.getAccountNumber(), - account.getCurrency().getCurrencyCode(), - account.getType().name(), - account.id - ) - ); + public void update(long accountId, AccountType type, String accountNumber, String name, Currency currency) { + DbUtil.doTransaction(conn, () -> { + Account account = findById(accountId).orElse(null); + if (account == null) return; + List updateMessages = new ArrayList<>(); + if (account.getType() != type) { + DbUtil.updateOne(conn, "UPDATE account SET account_type = ? WHERE id = ?", type, accountId); + updateMessages.add(String.format("Updated account type from %s to %s.", account.getType().toString(), type.toString())); + } + if (!account.getAccountNumber().equals(accountNumber)) { + DbUtil.updateOne(conn, "UPDATE account SET account_number = ? WHERE id = ?", accountNumber, accountId); + updateMessages.add(String.format("Updated account number from %s to %s.", account.getAccountNumber(), accountNumber)); + } + if (!account.getName().equals(name)) { + DbUtil.updateOne(conn, "UPDATE account SET name = ? WHERE id = ?", name, accountId); + updateMessages.add(String.format("Updated account name from \"%s\" to \"%s\".", account.getName(), name)); + } + if (account.getCurrency() != currency) { + DbUtil.updateOne(conn, "UPDATE account SET currency = ? WHERE id = ?", currency.getCurrencyCode(), accountId); + updateMessages.add(String.format("Updated account currency from %s to %s.", account.getCurrency(), currency)); + } + if (!updateMessages.isEmpty()) { + var historyRepo = new JdbcHistoryRepository(conn); + long historyId = historyRepo.getOrCreateHistoryForAccount(accountId); + historyRepo.addTextItem(historyId, String.join("\n", updateMessages)); + } + }); } @Override diff --git a/src/main/java/com/andrewlalis/perfin/model/Account.java b/src/main/java/com/andrewlalis/perfin/model/Account.java index dfb3db7..3cd4231 100644 --- a/src/main/java/com/andrewlalis/perfin/model/Account.java +++ b/src/main/java/com/andrewlalis/perfin/model/Account.java @@ -11,10 +11,10 @@ public class Account extends IdEntity { private final LocalDateTime createdAt; private final boolean archived; - private AccountType type; - private String accountNumber; - private String name; - private Currency currency; + private final AccountType type; + private final String accountNumber; + private final String name; + private final Currency currency; public Account(long id, LocalDateTime createdAt, boolean archived, AccountType type, String accountNumber, String name, Currency currency) { super(id); @@ -62,22 +62,6 @@ public class Account extends IdEntity { return currency; } - public void setType(AccountType type) { - this.type = type; - } - - public void setAccountNumber(String accountNumber) { - this.accountNumber = accountNumber; - } - - public void setName(String name) { - this.name = name; - } - - public void setCurrency(Currency currency) { - this.currency = currency; - } - public LocalDateTime getCreatedAt() { return createdAt; } diff --git a/src/main/java/com/andrewlalis/perfin/view/component/module/AccountsModule.java b/src/main/java/com/andrewlalis/perfin/view/component/module/AccountsModule.java index ecf5142..387ae92 100644 --- a/src/main/java/com/andrewlalis/perfin/view/component/module/AccountsModule.java +++ b/src/main/java/com/andrewlalis/perfin/view/component/module/AccountsModule.java @@ -70,7 +70,7 @@ public class AccountsModule extends DashboardModule { Label nameLabel = new Label(account.getName()); nameLabel.getStyleClass().addAll("bold-text"); - Label numberLabel = new Label(account.getAccountNumber()); + Label numberLabel = new Label(account.getAccountNumberSuffix()); numberLabel.getStyleClass().addAll("mono-font"); Label typeLabel = new Label(account.getType().toString()); typeLabel.getStyleClass().add("bold-text");