Added the ability to delete balance records, and added additional history handling for deleted information.
This commit is contained in:
parent
ed6e2fba4a
commit
087242396d
|
@ -43,6 +43,10 @@ public class AccountViewController implements RouteSelectionListener {
|
||||||
accountCreatedAtField.setText(DateUtil.formatUTCAsLocalWithZone(account.getCreatedAt()));
|
accountCreatedAtField.setText(DateUtil.formatUTCAsLocalWithZone(account.getCreatedAt()));
|
||||||
Profile.getCurrent().getDataSource().getAccountBalanceText(account, accountBalanceField::setText);
|
Profile.getCurrent().getDataSource().getAccountBalanceText(account, accountBalanceField::setText);
|
||||||
|
|
||||||
|
reloadHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reloadHistory() {
|
||||||
loadHistoryFrom = DateUtil.nowAsUTC();
|
loadHistoryFrom = DateUtil.nowAsUTC();
|
||||||
historyItemsVBox.getChildren().clear();
|
historyItemsVBox.getChildren().clear();
|
||||||
loadMoreHistoryButton.setDisable(false);
|
loadMoreHistoryButton.setDisable(false);
|
||||||
|
@ -106,7 +110,7 @@ public class AccountViewController implements RouteSelectionListener {
|
||||||
loadHistoryFrom = historyItems.getLast().getTimestamp();
|
loadHistoryFrom = historyItems.getLast().getTimestamp();
|
||||||
}
|
}
|
||||||
List<? extends Node> nodes = historyItems.stream()
|
List<? extends Node> nodes = historyItems.stream()
|
||||||
.map(item -> new AccountHistoryItemTile(item, historyRepo))
|
.map(item -> AccountHistoryItemTile.forItem(item, historyRepo, this))
|
||||||
.toList();
|
.toList();
|
||||||
Platform.runLater(() -> historyItemsVBox.getChildren().addAll(nodes));
|
Platform.runLater(() -> historyItemsVBox.getChildren().addAll(nodes));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -7,7 +7,10 @@ import com.andrewlalis.perfin.model.Profile;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.fxml.FXML;
|
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 javafx.scene.layout.VBox;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
@ -88,12 +91,7 @@ public class EditAccountController implements RouteSelectionListener {
|
||||||
BigDecimal initialBalance = new BigDecimal(initialBalanceField.getText().strip());
|
BigDecimal initialBalance = new BigDecimal(initialBalanceField.getText().strip());
|
||||||
List<Path> attachments = Collections.emptyList();
|
List<Path> attachments = Collections.emptyList();
|
||||||
|
|
||||||
Alert confirm = new Alert(
|
boolean success = Popups.confirm("Are you sure you want to create this account?");
|
||||||
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);
|
|
||||||
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);
|
||||||
|
|
|
@ -18,6 +18,7 @@ public interface AccountRepository extends AutoCloseable {
|
||||||
Page<Account> findAll(PageRequest pagination);
|
Page<Account> findAll(PageRequest pagination);
|
||||||
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(Account account);
|
void update(Account account);
|
||||||
void delete(Account account);
|
void delete(Account account);
|
||||||
void archive(Account account);
|
void archive(Account account);
|
||||||
|
|
|
@ -11,4 +11,5 @@ import java.util.List;
|
||||||
public interface BalanceRecordRepository extends AutoCloseable {
|
public interface BalanceRecordRepository extends AutoCloseable {
|
||||||
long insert(LocalDateTime utcTimestamp, long accountId, BigDecimal balance, Currency currency, List<Path> attachments);
|
long insert(LocalDateTime utcTimestamp, long accountId, BigDecimal balance, Currency currency, List<Path> attachments);
|
||||||
BalanceRecord findLatestByAccountId(long accountId);
|
BalanceRecord findLatestByAccountId(long accountId);
|
||||||
|
void deleteById(long id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,11 @@ public record JdbcAccountRepository(Connection conn) implements AccountRepositor
|
||||||
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 = ?", List.of(name, id));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BigDecimal deriveBalance(long id, Instant timestamp) {
|
public BigDecimal deriveBalance(long id, Instant timestamp) {
|
||||||
// Find the most recent balance record before timestamp.
|
// Find the most recent balance record before timestamp.
|
||||||
|
|
|
@ -51,6 +51,11 @@ public record JdbcBalanceRecordRepository(Connection conn, Path contentDir) impl
|
||||||
).orElse(null);
|
).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteById(long id) {
|
||||||
|
DbUtil.updateOne(conn, "DELETE FROM balance_record WHERE id = ?", List.of(id));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws Exception {
|
public void close() throws Exception {
|
||||||
conn.close();
|
conn.close();
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,26 +1,17 @@
|
||||||
package com.andrewlalis.perfin.view.component;
|
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.AccountHistoryItemRepository;
|
||||||
import com.andrewlalis.perfin.data.util.CurrencyUtil;
|
|
||||||
import com.andrewlalis.perfin.data.util.DateUtil;
|
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 com.andrewlalis.perfin.model.history.AccountHistoryItem;
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.Hyperlink;
|
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.BorderPane;
|
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.
|
* A tile that shows a brief bit of information about an account history item.
|
||||||
*/
|
*/
|
||||||
public class AccountHistoryItemTile extends BorderPane {
|
public abstract class AccountHistoryItemTile extends BorderPane {
|
||||||
public AccountHistoryItemTile(AccountHistoryItem item, AccountHistoryItemRepository repo) {
|
public AccountHistoryItemTile(AccountHistoryItem item) {
|
||||||
setStyle("""
|
setStyle("""
|
||||||
-fx-border-color: lightgray;
|
-fx-border-color: lightgray;
|
||||||
-fx-border-radius: 5px;
|
-fx-border-radius: 5px;
|
||||||
|
@ -30,33 +21,17 @@ public class AccountHistoryItemTile extends BorderPane {
|
||||||
Label timestampLabel = new Label(DateUtil.formatUTCAsLocalWithZone(item.getTimestamp()));
|
Label timestampLabel = new Label(DateUtil.formatUTCAsLocalWithZone(item.getTimestamp()));
|
||||||
timestampLabel.setStyle("-fx-font-size: small;");
|
timestampLabel.setStyle("-fx-font-size: small;");
|
||||||
setTop(timestampLabel);
|
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) {
|
public static AccountHistoryItemTile forItem(
|
||||||
return new TextFlow(new Text(text));
|
AccountHistoryItem item,
|
||||||
}
|
AccountHistoryItemRepository repo,
|
||||||
|
AccountViewController controller
|
||||||
private Node buildAccountEntryItem(AccountEntry entry) {
|
) {
|
||||||
Text amountText = new Text(CurrencyUtil.formatMoneyWithCurrencyPrefix(entry.getMoneyValue()));
|
return switch (item.getType()) {
|
||||||
Hyperlink transactionLink = new Hyperlink("Transaction #" + entry.getTransactionId());
|
case TEXT -> new AccountHistoryTextTile(item, repo);
|
||||||
transactionLink.setOnAction(event -> router.navigate(
|
case ACCOUNT_ENTRY -> new AccountHistoryAccountEntryTile(item, repo);
|
||||||
"transactions",
|
case BALANCE_RECORD -> new AccountHistoryBalanceRecordTile(item, repo, controller);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue