Added the ability to delete balance records, and added additional history handling for deleted information.

This commit is contained in:
Andrew Lalis 2024-01-03 18:38:29 -05:00
parent ed6e2fba4a
commit 087242396d
10 changed files with 125 additions and 46 deletions

View File

@ -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<? extends Node> 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) {

View File

@ -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<Path> attachments = Collections.emptyList();
Alert confirm = new Alert(
Alert.AlertType.CONFIRMATION,
"Are you sure you want to create this account?"
);
Optional<ButtonType> 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);

View File

@ -18,6 +18,7 @@ public interface AccountRepository extends AutoCloseable {
Page<Account> findAll(PageRequest pagination);
List<Account> findAllByCurrency(Currency currency);
Optional<Account> findById(long id);
void updateName(long id, String name);
void update(Account account);
void delete(Account account);
void archive(Account account);

View File

@ -11,4 +11,5 @@ import java.util.List;
public interface BalanceRecordRepository extends AutoCloseable {
long insert(LocalDateTime utcTimestamp, long accountId, BigDecimal balance, Currency currency, List<Path> attachments);
BalanceRecord findLatestByAccountId(long accountId);
void deleteById(long id);
}

View File

@ -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.

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
};
}
}

View File

@ -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)));
}
}