diff --git a/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java b/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java index 886fdf0..508844f 100644 --- a/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java +++ b/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java @@ -43,6 +43,10 @@ public class AccountViewController implements RouteSelectionListener { accountCreatedAtField.setText(DateUtil.formatUTCAsLocalWithZone(account.getCreatedAt())); Profile.getCurrent().getDataSource().getAccountBalanceText(account, accountBalanceField::setText); + reloadHistory(); + } + + public void reloadHistory() { loadHistoryFrom = DateUtil.nowAsUTC(); historyItemsVBox.getChildren().clear(); loadMoreHistoryButton.setDisable(false); @@ -106,7 +110,7 @@ public class AccountViewController implements RouteSelectionListener { loadHistoryFrom = historyItems.getLast().getTimestamp(); } List nodes = historyItems.stream() - .map(item -> new AccountHistoryItemTile(item, historyRepo)) + .map(item -> AccountHistoryItemTile.forItem(item, historyRepo, this)) .toList(); Platform.runLater(() -> historyItemsVBox.getChildren().addAll(nodes)); } catch (Exception e) { diff --git a/src/main/java/com/andrewlalis/perfin/control/EditAccountController.java b/src/main/java/com/andrewlalis/perfin/control/EditAccountController.java index 145eb23..8fd18f9 100644 --- a/src/main/java/com/andrewlalis/perfin/control/EditAccountController.java +++ b/src/main/java/com/andrewlalis/perfin/control/EditAccountController.java @@ -7,7 +7,10 @@ import com.andrewlalis.perfin.model.Profile; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.fxml.FXML; -import javafx.scene.control.*; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; import javafx.scene.layout.VBox; import java.math.BigDecimal; @@ -88,12 +91,7 @@ public class EditAccountController implements RouteSelectionListener { BigDecimal initialBalance = new BigDecimal(initialBalanceField.getText().strip()); List attachments = Collections.emptyList(); - Alert confirm = new Alert( - Alert.AlertType.CONFIRMATION, - "Are you sure you want to create this account?" - ); - Optional result = confirm.showAndWait(); - boolean success = result.isPresent() && result.get().equals(ButtonType.OK); + boolean success = Popups.confirm("Are you sure you want to create this account?"); if (success) { long id = accountRepo.insert(type, number, name, currency); balanceRepo.insert(LocalDateTime.now(ZoneOffset.UTC), id, initialBalance, currency, attachments); diff --git a/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java b/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java index fd19b8e..078ca34 100644 --- a/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java +++ b/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java @@ -18,6 +18,7 @@ public interface AccountRepository extends AutoCloseable { Page findAll(PageRequest pagination); List findAllByCurrency(Currency currency); Optional findById(long id); + void updateName(long id, String name); void update(Account account); void delete(Account account); void archive(Account account); diff --git a/src/main/java/com/andrewlalis/perfin/data/BalanceRecordRepository.java b/src/main/java/com/andrewlalis/perfin/data/BalanceRecordRepository.java index 82db27a..94a5d85 100644 --- a/src/main/java/com/andrewlalis/perfin/data/BalanceRecordRepository.java +++ b/src/main/java/com/andrewlalis/perfin/data/BalanceRecordRepository.java @@ -11,4 +11,5 @@ import java.util.List; public interface BalanceRecordRepository extends AutoCloseable { long insert(LocalDateTime utcTimestamp, long accountId, BigDecimal balance, Currency currency, List attachments); BalanceRecord findLatestByAccountId(long accountId); + void deleteById(long id); } 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 b8ee943..17cda4f 100644 --- a/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java +++ b/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java @@ -60,6 +60,11 @@ public record JdbcAccountRepository(Connection conn) implements AccountRepositor 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 = ?", List.of(name, id)); + } + @Override public BigDecimal deriveBalance(long id, Instant timestamp) { // Find the most recent balance record before timestamp. diff --git a/src/main/java/com/andrewlalis/perfin/data/impl/JdbcBalanceRecordRepository.java b/src/main/java/com/andrewlalis/perfin/data/impl/JdbcBalanceRecordRepository.java index f921895..6ded9ba 100644 --- a/src/main/java/com/andrewlalis/perfin/data/impl/JdbcBalanceRecordRepository.java +++ b/src/main/java/com/andrewlalis/perfin/data/impl/JdbcBalanceRecordRepository.java @@ -51,6 +51,11 @@ public record JdbcBalanceRecordRepository(Connection conn, Path contentDir) impl ).orElse(null); } + @Override + public void deleteById(long id) { + DbUtil.updateOne(conn, "DELETE FROM balance_record WHERE id = ?", List.of(id)); + } + @Override public void close() throws Exception { conn.close(); diff --git a/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryAccountEntryTile.java b/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryAccountEntryTile.java new file mode 100644 index 0000000..20b01c1 --- /dev/null +++ b/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryAccountEntryTile.java @@ -0,0 +1,36 @@ +package com.andrewlalis.perfin.view.component; + +import com.andrewlalis.perfin.control.TransactionsViewController; +import com.andrewlalis.perfin.data.AccountHistoryItemRepository; +import com.andrewlalis.perfin.data.util.CurrencyUtil; +import com.andrewlalis.perfin.model.AccountEntry; +import com.andrewlalis.perfin.model.history.AccountHistoryItem; +import javafx.scene.control.Hyperlink; +import javafx.scene.text.Text; +import javafx.scene.text.TextFlow; + +import static com.andrewlalis.perfin.PerfinApp.router; + +public class AccountHistoryAccountEntryTile extends AccountHistoryItemTile { + public AccountHistoryAccountEntryTile(AccountHistoryItem item, AccountHistoryItemRepository repo) { + super(item); + AccountEntry entry = repo.getAccountEntryItem(item.getId()); + if (entry == null) { + setCenter(new TextFlow(new Text("Deleted account entry because of deleted transaction."))); + return; + } + + Text amountText = new Text(CurrencyUtil.formatMoneyWithCurrencyPrefix(entry.getMoneyValue())); + Hyperlink transactionLink = new Hyperlink("Transaction #" + entry.getTransactionId()); + transactionLink.setOnAction(event -> router.navigate( + "transactions", + new TransactionsViewController.RouteContext(entry.getTransactionId()) + )); + var text = new TextFlow( + transactionLink, + new Text("posted as a " + entry.getType().name().toLowerCase() + " to this account, with a value of "), + amountText + ); + setCenter(text); + } +} diff --git a/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryBalanceRecordTile.java b/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryBalanceRecordTile.java new file mode 100644 index 0000000..11cb5fa --- /dev/null +++ b/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryBalanceRecordTile.java @@ -0,0 +1,40 @@ +package com.andrewlalis.perfin.view.component; + +import com.andrewlalis.perfin.control.AccountViewController; +import com.andrewlalis.perfin.control.Popups; +import com.andrewlalis.perfin.data.AccountHistoryItemRepository; +import com.andrewlalis.perfin.data.util.CurrencyUtil; +import com.andrewlalis.perfin.model.BalanceRecord; +import com.andrewlalis.perfin.model.Profile; +import com.andrewlalis.perfin.model.history.AccountHistoryItem; +import javafx.application.Platform; +import javafx.scene.control.Hyperlink; +import javafx.scene.text.Text; +import javafx.scene.text.TextFlow; + +public class AccountHistoryBalanceRecordTile extends AccountHistoryItemTile { + public AccountHistoryBalanceRecordTile(AccountHistoryItem item, AccountHistoryItemRepository repo, AccountViewController controller) { + super(item); + BalanceRecord balanceRecord = repo.getBalanceRecordItem(item.getId()); + if (balanceRecord == null) { + setCenter(new TextFlow(new Text("Deleted balance record was added."))); + return; + } + + Text amountText = new Text(CurrencyUtil.formatMoneyWithCurrencyPrefix(balanceRecord.getMoneyAmount())); + var text = new TextFlow(new Text("Balance record #" + balanceRecord.getId() + " added with value of "), amountText); + setCenter(text); + + Hyperlink deleteLink = new Hyperlink("Delete this balance record"); + deleteLink.setOnAction(event -> { + boolean confirm = Popups.confirm("Are you sure you want to delete this balance record? It will be removed permanently, and cannot be undone."); + if (confirm) { + Profile.getCurrent().getDataSource().useBalanceRecordRepository(balanceRecordRepo -> { + balanceRecordRepo.deleteById(balanceRecord.getId()); + Platform.runLater(controller::reloadHistory); + }); + } + }); + setBottom(deleteLink); + } +} diff --git a/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryItemTile.java b/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryItemTile.java index 9f4f5e7..81d1359 100644 --- a/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryItemTile.java +++ b/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryItemTile.java @@ -1,26 +1,17 @@ package com.andrewlalis.perfin.view.component; -import com.andrewlalis.perfin.control.TransactionsViewController; +import com.andrewlalis.perfin.control.AccountViewController; import com.andrewlalis.perfin.data.AccountHistoryItemRepository; -import com.andrewlalis.perfin.data.util.CurrencyUtil; import com.andrewlalis.perfin.data.util.DateUtil; -import com.andrewlalis.perfin.model.AccountEntry; -import com.andrewlalis.perfin.model.BalanceRecord; import com.andrewlalis.perfin.model.history.AccountHistoryItem; -import javafx.scene.Node; -import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import javafx.scene.layout.BorderPane; -import javafx.scene.text.Text; -import javafx.scene.text.TextFlow; - -import static com.andrewlalis.perfin.PerfinApp.router; /** * A tile that shows a brief bit of information about an account history item. */ -public class AccountHistoryItemTile extends BorderPane { - public AccountHistoryItemTile(AccountHistoryItem item, AccountHistoryItemRepository repo) { +public abstract class AccountHistoryItemTile extends BorderPane { + public AccountHistoryItemTile(AccountHistoryItem item) { setStyle(""" -fx-border-color: lightgray; -fx-border-radius: 5px; @@ -30,33 +21,17 @@ public class AccountHistoryItemTile extends BorderPane { Label timestampLabel = new Label(DateUtil.formatUTCAsLocalWithZone(item.getTimestamp())); timestampLabel.setStyle("-fx-font-size: small;"); setTop(timestampLabel); - setCenter(switch (item.getType()) { - case TEXT -> buildTextItem(repo.getTextItem(item.getId())); - case ACCOUNT_ENTRY -> buildAccountEntryItem(repo.getAccountEntryItem(item.getId())); - case BALANCE_RECORD -> buildBalanceRecordItem(repo.getBalanceRecordItem(item.getId())); - }); } - private Node buildTextItem(String text) { - return new TextFlow(new Text(text)); - } - - private Node buildAccountEntryItem(AccountEntry entry) { - Text amountText = new Text(CurrencyUtil.formatMoneyWithCurrencyPrefix(entry.getMoneyValue())); - Hyperlink transactionLink = new Hyperlink("Transaction #" + entry.getTransactionId()); - transactionLink.setOnAction(event -> router.navigate( - "transactions", - new TransactionsViewController.RouteContext(entry.getTransactionId()) - )); - return new TextFlow( - transactionLink, - new Text("posted as a " + entry.getType().name().toLowerCase() + " to this account, with a value of "), - amountText - ); - } - - private Node buildBalanceRecordItem(BalanceRecord balanceRecord) { - Text amountText = new Text(CurrencyUtil.formatMoneyWithCurrencyPrefix(balanceRecord.getMoneyAmount())); - return new TextFlow(new Text("Balance record #" + balanceRecord.getId() + " added with value of "), amountText); + public static AccountHistoryItemTile forItem( + AccountHistoryItem item, + AccountHistoryItemRepository repo, + AccountViewController controller + ) { + return switch (item.getType()) { + case TEXT -> new AccountHistoryTextTile(item, repo); + case ACCOUNT_ENTRY -> new AccountHistoryAccountEntryTile(item, repo); + case BALANCE_RECORD -> new AccountHistoryBalanceRecordTile(item, repo, controller); + }; } } diff --git a/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryTextTile.java b/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryTextTile.java new file mode 100644 index 0000000..1fecb55 --- /dev/null +++ b/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryTextTile.java @@ -0,0 +1,14 @@ +package com.andrewlalis.perfin.view.component; + +import com.andrewlalis.perfin.data.AccountHistoryItemRepository; +import com.andrewlalis.perfin.model.history.AccountHistoryItem; +import javafx.scene.text.Text; +import javafx.scene.text.TextFlow; + +public class AccountHistoryTextTile extends AccountHistoryItemTile { + public AccountHistoryTextTile(AccountHistoryItem item, AccountHistoryItemRepository repo) { + super(item); + String text = repo.getTextItem(item.getId()); + setCenter(new TextFlow(new Text(text))); + } +}