Added account filter to transactions list.
This commit is contained in:
parent
8539ddec70
commit
9e0e0a51c5
|
@ -5,21 +5,26 @@ import com.andrewlalis.perfin.data.pagination.Page;
|
||||||
import com.andrewlalis.perfin.data.pagination.PageRequest;
|
import com.andrewlalis.perfin.data.pagination.PageRequest;
|
||||||
import com.andrewlalis.perfin.data.pagination.Sort;
|
import com.andrewlalis.perfin.data.pagination.Sort;
|
||||||
import com.andrewlalis.perfin.data.util.Pair;
|
import com.andrewlalis.perfin.data.util.Pair;
|
||||||
|
import com.andrewlalis.perfin.model.Account;
|
||||||
import com.andrewlalis.perfin.model.Profile;
|
import com.andrewlalis.perfin.model.Profile;
|
||||||
import com.andrewlalis.perfin.model.Transaction;
|
import com.andrewlalis.perfin.model.Transaction;
|
||||||
|
import com.andrewlalis.perfin.view.AccountComboBoxCellFactory;
|
||||||
import com.andrewlalis.perfin.view.SceneUtil;
|
import com.andrewlalis.perfin.view.SceneUtil;
|
||||||
import com.andrewlalis.perfin.view.component.DataSourcePaginationControls;
|
import com.andrewlalis.perfin.view.component.DataSourcePaginationControls;
|
||||||
import com.andrewlalis.perfin.view.component.TransactionTile;
|
import com.andrewlalis.perfin.view.component.TransactionTile;
|
||||||
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.ComboBox;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static com.andrewlalis.perfin.PerfinApp.router;
|
import static com.andrewlalis.perfin.PerfinApp.router;
|
||||||
|
|
||||||
|
@ -35,6 +40,7 @@ public class TransactionsViewController implements RouteSelectionListener {
|
||||||
public record RouteContext(Long selectedTransactionId) {}
|
public record RouteContext(Long selectedTransactionId) {}
|
||||||
|
|
||||||
@FXML public BorderPane transactionsListBorderPane;
|
@FXML public BorderPane transactionsListBorderPane;
|
||||||
|
@FXML public ComboBox<Account> filterByAccountComboBox;
|
||||||
@FXML public VBox transactionsVBox;
|
@FXML public VBox transactionsVBox;
|
||||||
private DataSourcePaginationControls paginationControls;
|
private DataSourcePaginationControls paginationControls;
|
||||||
|
|
||||||
|
@ -44,20 +50,40 @@ public class TransactionsViewController implements RouteSelectionListener {
|
||||||
|
|
||||||
@FXML public void initialize() {
|
@FXML public void initialize() {
|
||||||
// Initialize the left-hand paginated transactions list.
|
// Initialize the left-hand paginated transactions list.
|
||||||
|
var accountCellFactory = new AccountComboBoxCellFactory("All");
|
||||||
|
filterByAccountComboBox.setCellFactory(accountCellFactory);
|
||||||
|
filterByAccountComboBox.setButtonCell(accountCellFactory.call(null));
|
||||||
|
filterByAccountComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
paginationControls.setPage(1);
|
||||||
|
selectedTransaction.set(null);
|
||||||
|
});
|
||||||
|
|
||||||
this.paginationControls = new DataSourcePaginationControls(
|
this.paginationControls = new DataSourcePaginationControls(
|
||||||
transactionsVBox.getChildren(),
|
transactionsVBox.getChildren(),
|
||||||
new DataSourcePaginationControls.PageFetcherFunction() {
|
new DataSourcePaginationControls.PageFetcherFunction() {
|
||||||
@Override
|
@Override
|
||||||
public Page<? extends Node> fetchPage(PageRequest pagination) throws Exception {
|
public Page<? extends Node> fetchPage(PageRequest pagination) throws Exception {
|
||||||
|
Account accountFilter = filterByAccountComboBox.getValue();
|
||||||
try (var repo = Profile.getCurrent().getDataSource().getTransactionRepository()) {
|
try (var repo = Profile.getCurrent().getDataSource().getTransactionRepository()) {
|
||||||
return repo.findAll(pagination).map(TransactionsViewController.this::makeTile);
|
Page<Transaction> result;
|
||||||
|
if (accountFilter == null) {
|
||||||
|
result = repo.findAll(pagination);
|
||||||
|
} else {
|
||||||
|
result = repo.findAllByAccounts(Set.of(accountFilter.id), pagination);
|
||||||
|
}
|
||||||
|
return result.map(TransactionsViewController.this::makeTile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getTotalCount() throws Exception {
|
public int getTotalCount() throws Exception {
|
||||||
|
Account accountFilter = filterByAccountComboBox.getValue();
|
||||||
try (var repo = Profile.getCurrent().getDataSource().getTransactionRepository()) {
|
try (var repo = Profile.getCurrent().getDataSource().getTransactionRepository()) {
|
||||||
return (int) repo.countAll();
|
if (accountFilter == null) {
|
||||||
|
return (int) repo.countAll();
|
||||||
|
} else {
|
||||||
|
return (int) repo.countAllByAccounts(Set.of(accountFilter.id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,6 +121,21 @@ public class TransactionsViewController implements RouteSelectionListener {
|
||||||
paginationControls.sorts.setAll(DEFAULT_SORTS);
|
paginationControls.sorts.setAll(DEFAULT_SORTS);
|
||||||
paginationControls.itemsPerPage.set(DEFAULT_ITEMS_PER_PAGE);
|
paginationControls.itemsPerPage.set(DEFAULT_ITEMS_PER_PAGE);
|
||||||
|
|
||||||
|
// Refresh account filter options.
|
||||||
|
Thread.ofVirtual().start(() -> {
|
||||||
|
Profile.getCurrent().getDataSource().useAccountRepository(repo -> {
|
||||||
|
List<Account> accounts = repo.findAll(PageRequest.unpaged(Sort.asc("name"))).items();
|
||||||
|
accounts.add(null);
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
filterByAccountComboBox.getItems().clear();
|
||||||
|
filterByAccountComboBox.getItems().addAll(accounts);
|
||||||
|
filterByAccountComboBox.getSelectionModel().selectLast();
|
||||||
|
filterByAccountComboBox.getButtonCell().updateIndex(accounts.size() - 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// If a transaction id is given in the route context, navigate to the page it's on and select it.
|
// If a transaction id is given in the route context, navigate to the page it's on and select it.
|
||||||
if (context instanceof RouteContext ctx && ctx.selectedTransactionId != null) {
|
if (context instanceof RouteContext ctx && ctx.selectedTransactionId != null) {
|
||||||
Thread.ofVirtual().start(() -> {
|
Thread.ofVirtual().start(() -> {
|
||||||
|
@ -108,6 +149,7 @@ public class TransactionsViewController implements RouteSelectionListener {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
paginationControls.setPage(1);
|
paginationControls.setPage(1);
|
||||||
|
selectedTransaction.set(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ public interface TransactionRepository extends AutoCloseable {
|
||||||
Page<Transaction> findAll(PageRequest pagination);
|
Page<Transaction> findAll(PageRequest pagination);
|
||||||
long countAll();
|
long countAll();
|
||||||
long countAllAfter(long transactionId);
|
long countAllAfter(long transactionId);
|
||||||
|
long countAllByAccounts(Set<Long> accountIds);
|
||||||
Page<Transaction> findAllByAccounts(Set<Long> accountIds, PageRequest pagination);
|
Page<Transaction> findAllByAccounts(Set<Long> accountIds, PageRequest pagination);
|
||||||
CreditAndDebitAccounts findLinkedAccounts(long transactionId);
|
CreditAndDebitAccounts findLinkedAccounts(long transactionId);
|
||||||
List<Attachment> findAttachments(long transactionId);
|
List<Attachment> findAttachments(long transactionId);
|
||||||
|
|
|
@ -82,6 +82,18 @@ public record JdbcTransactionRepository(Connection conn, Path contentDir) implem
|
||||||
).orElse(0L);
|
).orElse(0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long countAllByAccounts(Set<Long> accountIds) {
|
||||||
|
String idsStr = accountIds.stream().map(String::valueOf).collect(Collectors.joining(","));
|
||||||
|
String query = String.format("""
|
||||||
|
SELECT COUNT(transaction.id)
|
||||||
|
FROM transaction
|
||||||
|
LEFT JOIN account_entry ON account_entry.transaction_id = transaction.id
|
||||||
|
WHERE account_entry.account_id IN (%s)
|
||||||
|
""", idsStr);
|
||||||
|
return DbUtil.findOne(conn, query, Collections.emptyList(), rs -> rs.getLong(1)).orElse(0L);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<Transaction> findAllByAccounts(Set<Long> accountIds, PageRequest pagination) {
|
public Page<Transaction> findAllByAccounts(Set<Long> accountIds, PageRequest pagination) {
|
||||||
String idsStr = accountIds.stream().map(String::valueOf).collect(Collectors.joining(","));
|
String idsStr = accountIds.stream().map(String::valueOf).collect(Collectors.joining(","));
|
||||||
|
|
|
@ -7,10 +7,22 @@ import javafx.scene.control.ListView;
|
||||||
import javafx.util.Callback;
|
import javafx.util.Callback;
|
||||||
|
|
||||||
public class AccountComboBoxCellFactory implements Callback<ListView<Account>, ListCell<Account>> {
|
public class AccountComboBoxCellFactory implements Callback<ListView<Account>, ListCell<Account>> {
|
||||||
|
private final String emptyCellText;
|
||||||
|
|
||||||
|
public AccountComboBoxCellFactory(String emptyCellText) {
|
||||||
|
this.emptyCellText = emptyCellText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountComboBoxCellFactory() {
|
||||||
|
this("None");
|
||||||
|
}
|
||||||
|
|
||||||
public static class AccountListCell extends ListCell<Account> {
|
public static class AccountListCell extends ListCell<Account> {
|
||||||
private final Label label = new Label();
|
private final Label label = new Label();
|
||||||
|
private final String emptyCellText;
|
||||||
|
|
||||||
public AccountListCell() {
|
public AccountListCell(String emptyCellText) {
|
||||||
|
this.emptyCellText = emptyCellText;
|
||||||
label.setStyle("-fx-text-fill: black;");
|
label.setStyle("-fx-text-fill: black;");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +30,7 @@ public class AccountComboBoxCellFactory implements Callback<ListView<Account>, L
|
||||||
protected void updateItem(Account item, boolean empty) {
|
protected void updateItem(Account item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
if (item == null || empty) {
|
if (item == null || empty) {
|
||||||
label.setText("None");
|
label.setText(emptyCellText);
|
||||||
} else {
|
} else {
|
||||||
label.setText(item.getName() + " (" + item.getAccountNumberSuffix() + ")");
|
label.setText(item.getName() + " (" + item.getAccountNumberSuffix() + ")");
|
||||||
}
|
}
|
||||||
|
@ -28,6 +40,6 @@ public class AccountComboBoxCellFactory implements Callback<ListView<Account>, L
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListCell<Account> call(ListView<Account> param) {
|
public ListCell<Account> call(ListView<Account> param) {
|
||||||
return new AccountListCell();
|
return new AccountListCell(emptyCellText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import com.andrewlalis.perfin.view.component.PropertiesPane?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.ScrollPane?>
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<BorderPane xmlns="http://javafx.com/javafx"
|
<BorderPane xmlns="http://javafx.com/javafx"
|
||||||
|
@ -15,6 +18,14 @@
|
||||||
<center>
|
<center>
|
||||||
<HBox>
|
<HBox>
|
||||||
<BorderPane fx:id="transactionsListBorderPane" HBox.hgrow="ALWAYS">
|
<BorderPane fx:id="transactionsListBorderPane" HBox.hgrow="ALWAYS">
|
||||||
|
<top>
|
||||||
|
<HBox styleClass="std-padding,std-spacing">
|
||||||
|
<PropertiesPane hgap="5" vgap="5">
|
||||||
|
<Label text="Filter by Account"/>
|
||||||
|
<ComboBox fx:id="filterByAccountComboBox"/>
|
||||||
|
</PropertiesPane>
|
||||||
|
</HBox>
|
||||||
|
</top>
|
||||||
<center>
|
<center>
|
||||||
<ScrollPane fitToHeight="true" fitToWidth="true">
|
<ScrollPane fitToHeight="true" fitToWidth="true">
|
||||||
<VBox fx:id="transactionsVBox" styleClass="std-padding,spacing-extra"/>
|
<VBox fx:id="transactionsVBox" styleClass="std-padding,spacing-extra"/>
|
||||||
|
|
Loading…
Reference in New Issue