Fixed account editing.

This commit is contained in:
Andrew Lalis 2024-02-06 17:59:13 -05:00
parent 807259b2a5
commit abf132ec99
5 changed files with 52 additions and 56 deletions

View File

@ -1,8 +1,10 @@
package com.andrewlalis.perfin.control; package com.andrewlalis.perfin.control;
import com.andrewlalis.javafx_scene_router.RouteSelectionListener; 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.Account;
import com.andrewlalis.perfin.model.AccountType; import com.andrewlalis.perfin.model.AccountType;
import com.andrewlalis.perfin.model.MoneyValue;
import com.andrewlalis.perfin.model.Profile; import com.andrewlalis.perfin.model.Profile;
import com.andrewlalis.perfin.view.component.PropertiesPane; import com.andrewlalis.perfin.view.component.PropertiesPane;
import com.andrewlalis.perfin.view.component.validation.ValidationApplier; import com.andrewlalis.perfin.view.component.validation.ValidationApplier;
@ -52,20 +54,20 @@ public class EditAccountController implements RouteSelectionListener {
public void initialize() { public void initialize() {
var nameValid = new ValidationApplier<>(new PredicateValidator<String>() var nameValid = new ValidationApplier<>(new PredicateValidator<String>()
.addTerminalPredicate(s -> s != null && !s.isBlank(), "Name should not be empty.") .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); ).attachToTextField(accountNameField);
var numberValid = new ValidationApplier<>(new PredicateValidator<String>() var numberValid = new ValidationApplier<>(new PredicateValidator<String>()
.addTerminalPredicate(s -> s != null && !s.isBlank(), "Account number should not be empty.") .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); ).attachToTextField(accountNumberField);
var balanceValid = new ValidationApplier<>( var balanceValid = new ValidationApplier<>(
new CurrencyAmountValidator(() -> accountCurrencyComboBox.getValue(), false, false) new CurrencyAmountValidator(() -> accountCurrencyComboBox.getValue(), true, false)
).attachToTextField(initialBalanceField, accountCurrencyComboBox.valueProperty()); ).attachToTextField(initialBalanceField, accountCurrencyComboBox.valueProperty());
// Combine validity of all fields for an expression that determines if the whole form is valid. // 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()); saveButton.disableProperty().bind(formValid.not());
List<Currency> priorityCurrencies = Stream.of("USD", "EUR", "GBP", "CAD", "AUD") List<Currency> priorityCurrencies = Stream.of("USD", "EUR", "GBP", "CAD", "AUD")
@ -104,34 +106,35 @@ public class EditAccountController implements RouteSelectionListener {
@FXML @FXML
public void save() { public void save() {
String name = accountNameField.getText().strip();
String number = accountNumberField.getText().strip();
AccountType type = accountTypeChoiceBox.getValue();
Currency currency = accountCurrencyComboBox.getValue();
try ( try (
var accountRepo = Profile.getCurrent().dataSource().getAccountRepository(); var accountRepo = Profile.getCurrent().dataSource().getAccountRepository();
var balanceRepo = Profile.getCurrent().dataSource().getBalanceRecordRepository() var balanceRepo = Profile.getCurrent().dataSource().getBalanceRecordRepository()
) { ) {
if (creatingNewAccount.get()) { 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()); BigDecimal initialBalance = new BigDecimal(initialBalanceField.getText().strip());
List<Path> attachments = Collections.emptyList(); List<Path> 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) { if (success) {
long id = accountRepo.insert(type, number, name, currency); long id = accountRepo.insert(type, number, name, currency);
balanceRepo.insert(LocalDateTime.now(ZoneOffset.UTC), id, initialBalance, currency, attachments); balanceRepo.insert(LocalDateTime.now(ZoneOffset.UTC), id, initialBalance, currency, attachments);
// Once we create the new account, go to the account. // Once we create the new account, go to the account.
Account newAccount = accountRepo.findById(id).orElseThrow(); Account newAccount = accountRepo.findById(id).orElseThrow();
router.replace("account", newAccount); router.replace("account", newAccount);
} }
} else { } else {
log.debug("Updating account {}", account.id); accountRepo.update(account.id, type, number, name, currency);
account.setName(accountNameField.getText().strip());
account.setAccountNumber(accountNumberField.getText().strip());
account.setType(accountTypeChoiceBox.getValue());
account.setCurrency(accountCurrencyComboBox.getValue());
accountRepo.update(account);
Account updatedAccount = accountRepo.findById(account.id).orElseThrow(); Account updatedAccount = accountRepo.findById(account.id).orElseThrow();
router.replace("account", updatedAccount); router.replace("account", updatedAccount);
} }

View File

@ -23,8 +23,7 @@ public interface AccountRepository extends Repository, AutoCloseable {
List<Account> findTopNRecentlyActive(int n, int daysSinceLastActive); List<Account> findTopNRecentlyActive(int n, int daysSinceLastActive);
List<Account> findAllByCurrency(Currency currency); List<Account> findAllByCurrency(Currency currency);
Optional<Account> findById(long id); Optional<Account> findById(long id);
void updateName(long id, String name); void update(long accountId, AccountType type, String accountNumber, String name, Currency currency);
void update(Account account);
void delete(Account account); void delete(Account account);
void archive(long accountId); void archive(long accountId);
void unarchive(long accountId); void unarchive(long accountId);

View File

@ -113,11 +113,6 @@ public record JdbcAccountRepository(Connection conn, Path contentDir) implements
return DbUtil.findById(conn, "SELECT * FROM account WHERE id = ?", id, JdbcAccountRepository::parseAccount); 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 @Override
public BigDecimal deriveBalance(long accountId, Instant timestamp) { public BigDecimal deriveBalance(long accountId, Instant timestamp) {
// First find the account itself, since its properties influence the balance. // First find the account itself, since its properties influence the balance.
@ -215,18 +210,33 @@ public record JdbcAccountRepository(Connection conn, Path contentDir) implements
} }
@Override @Override
public void update(Account account) { public void update(long accountId, AccountType type, String accountNumber, String name, Currency currency) {
DbUtil.updateOne( DbUtil.doTransaction(conn, () -> {
conn, Account account = findById(accountId).orElse(null);
"UPDATE account SET name = ?, account_number = ?, currency = ?, account_type = ? WHERE id = ?", if (account == null) return;
List.of( List<String> updateMessages = new ArrayList<>();
account.getName(), if (account.getType() != type) {
account.getAccountNumber(), DbUtil.updateOne(conn, "UPDATE account SET account_type = ? WHERE id = ?", type, accountId);
account.getCurrency().getCurrencyCode(), updateMessages.add(String.format("Updated account type from %s to %s.", account.getType().toString(), type.toString()));
account.getType().name(), }
account.id 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 @Override

View File

@ -11,10 +11,10 @@ public class Account extends IdEntity {
private final LocalDateTime createdAt; private final LocalDateTime createdAt;
private final boolean archived; private final boolean archived;
private AccountType type; private final AccountType type;
private String accountNumber; private final String accountNumber;
private String name; private final String name;
private Currency currency; private final Currency currency;
public Account(long id, LocalDateTime createdAt, boolean archived, AccountType type, String accountNumber, String name, Currency currency) { public Account(long id, LocalDateTime createdAt, boolean archived, AccountType type, String accountNumber, String name, Currency currency) {
super(id); super(id);
@ -62,22 +62,6 @@ public class Account extends IdEntity {
return currency; 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() { public LocalDateTime getCreatedAt() {
return createdAt; return createdAt;
} }

View File

@ -70,7 +70,7 @@ public class AccountsModule extends DashboardModule {
Label nameLabel = new Label(account.getName()); Label nameLabel = new Label(account.getName());
nameLabel.getStyleClass().addAll("bold-text"); nameLabel.getStyleClass().addAll("bold-text");
Label numberLabel = new Label(account.getAccountNumber()); Label numberLabel = new Label(account.getAccountNumberSuffix());
numberLabel.getStyleClass().addAll("mono-font"); numberLabel.getStyleClass().addAll("mono-font");
Label typeLabel = new Label(account.getType().toString()); Label typeLabel = new Label(account.getType().toString());
typeLabel.getStyleClass().add("bold-text"); typeLabel.getStyleClass().add("bold-text");