Added transaction descriptions to account history tiles.
This commit is contained in:
parent
4cf95dba85
commit
5ce2360f05
|
@ -179,7 +179,7 @@ public record JdbcAccountRepository(Connection conn, Path contentDir) implements
|
||||||
SELECT id, timestamp, 'BALANCE_RECORD' AS type, account_id
|
SELECT id, timestamp, 'BALANCE_RECORD' AS type, account_id
|
||||||
FROM balance_record
|
FROM balance_record
|
||||||
)
|
)
|
||||||
WHERE account_id = ? AND timestamp <= ?
|
WHERE account_id = ? AND timestamp < ?
|
||||||
ORDER BY timestamp DESC
|
ORDER BY timestamp DESC
|
||||||
LIMIT\s""" + maxResults;
|
LIMIT\s""" + maxResults;
|
||||||
try (var stmt = conn.prepareStatement(query)) {
|
try (var stmt = conn.prepareStatement(query)) {
|
||||||
|
|
|
@ -3,12 +3,10 @@ package com.andrewlalis.perfin.view.component;
|
||||||
import com.andrewlalis.perfin.control.TransactionsViewController;
|
import com.andrewlalis.perfin.control.TransactionsViewController;
|
||||||
import com.andrewlalis.perfin.data.AccountRepository;
|
import com.andrewlalis.perfin.data.AccountRepository;
|
||||||
import com.andrewlalis.perfin.data.DataSource;
|
import com.andrewlalis.perfin.data.DataSource;
|
||||||
|
import com.andrewlalis.perfin.data.TransactionRepository;
|
||||||
import com.andrewlalis.perfin.data.util.CurrencyUtil;
|
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.*;
|
||||||
import com.andrewlalis.perfin.model.BalanceRecord;
|
|
||||||
import com.andrewlalis.perfin.model.Profile;
|
|
||||||
import com.andrewlalis.perfin.model.Timestamped;
|
|
||||||
import com.andrewlalis.perfin.model.history.HistoryTextItem;
|
import com.andrewlalis.perfin.model.history.HistoryTextItem;
|
||||||
import com.andrewlalis.perfin.view.BindingUtil;
|
import com.andrewlalis.perfin.view.BindingUtil;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
@ -27,6 +25,8 @@ import javafx.scene.text.TextFlow;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import static com.andrewlalis.perfin.PerfinApp.router;
|
import static com.andrewlalis.perfin.PerfinApp.router;
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ public class AccountHistoryView extends ScrollPane {
|
||||||
int maxItems = initialItemsToLoadProperty.get();
|
int maxItems = initialItemsToLoadProperty.get();
|
||||||
DataSource ds = Profile.getCurrent().dataSource();
|
DataSource ds = Profile.getCurrent().dataSource();
|
||||||
ds.mapRepoAsync(AccountRepository.class, repo -> repo.findEventsBefore(accountId, lastTimestamp(), maxItems))
|
ds.mapRepoAsync(AccountRepository.class, repo -> repo.findEventsBefore(accountId, lastTimestamp(), maxItems))
|
||||||
.thenAccept(entities -> Platform.runLater(() -> addEntitiesToHistory(entities, maxItems)));
|
.thenAccept(entities -> addEntitiesToHistory(entities, maxItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
|
@ -91,56 +91,75 @@ public class AccountHistoryView extends ScrollPane {
|
||||||
return lastTimestamp;
|
return lastTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node makeTile(Timestamped entity) {
|
private CompletableFuture<Node> makeTile(Timestamped entity) {
|
||||||
switch (entity) {
|
switch (entity) {
|
||||||
case HistoryTextItem textItem -> {
|
case HistoryTextItem textItem -> {
|
||||||
return new AccountHistoryTile(textItem.getTimestamp(), new TextFlow(new Text(textItem.getDescription())));
|
return CompletableFuture.completedFuture(
|
||||||
|
new AccountHistoryTile(textItem.getTimestamp(), new TextFlow(new Text(textItem.getDescription())))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case AccountEntry ae -> {
|
case AccountEntry ae -> {
|
||||||
Hyperlink txLink = new Hyperlink("Transaction #" + ae.getTransactionId());
|
Hyperlink txLink = new Hyperlink("Transaction #" + ae.getTransactionId());
|
||||||
txLink.setOnAction(event -> router.navigate("transactions", new TransactionsViewController.RouteContext(ae.getTransactionId())));
|
txLink.setOnAction(event -> router.navigate("transactions", new TransactionsViewController.RouteContext(ae.getTransactionId())));
|
||||||
String descriptionFormat = ae.getType() == AccountEntry.Type.CREDIT
|
String descriptionFormat = ae.getType() == AccountEntry.Type.CREDIT
|
||||||
? "credited %s from this account."
|
? "credited %s from this account"
|
||||||
: "debited %s to this account.";
|
: "debited %s to this account";
|
||||||
String description = descriptionFormat.formatted(CurrencyUtil.formatMoney(ae.getMoneyValue()));
|
final String description = descriptionFormat.formatted(CurrencyUtil.formatMoney(ae.getMoneyValue()));
|
||||||
TextFlow textFlow = new TextFlow(txLink, new Text(description));
|
|
||||||
return new AccountHistoryTile(ae.getTimestamp(), textFlow);
|
CompletableFuture<Node> future = new CompletableFuture<>();
|
||||||
|
Profile.getCurrent().dataSource().useRepoAsync(TransactionRepository.class, repo -> {
|
||||||
|
Optional<Transaction> optionalTransaction = repo.findById(ae.getTransactionId());
|
||||||
|
String extraText = optionalTransaction.map(transaction -> ": " + transaction.getDescription())
|
||||||
|
.orElse(". No transaction information found.");
|
||||||
|
TextFlow textFlow = new TextFlow(txLink, new Text(description + extraText));
|
||||||
|
future.complete(new AccountHistoryTile(ae.getTimestamp(), textFlow));
|
||||||
|
});
|
||||||
|
return future;
|
||||||
|
|
||||||
}
|
}
|
||||||
case BalanceRecord br -> {
|
case BalanceRecord br -> {
|
||||||
Hyperlink brLink = new Hyperlink("Balance Record #" + br.id);
|
Hyperlink brLink = new Hyperlink("Balance Record #" + br.id);
|
||||||
brLink.setOnAction(event -> router.navigate("balance-record", br));
|
brLink.setOnAction(event -> router.navigate("balance-record", br));
|
||||||
return new AccountHistoryTile(br.getTimestamp(), new TextFlow(
|
return CompletableFuture.completedFuture(new AccountHistoryTile(br.getTimestamp(), new TextFlow(
|
||||||
brLink,
|
brLink,
|
||||||
new Text("added with a value of %s.".formatted(CurrencyUtil.formatMoney(br.getMoneyAmount())))
|
new Text("added with a value of %s.".formatted(CurrencyUtil.formatMoney(br.getMoneyAmount())))
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
default -> {
|
default -> {
|
||||||
return new AccountHistoryTile(entity.getTimestamp(), new TextFlow(new Text("Unsupported entity: " + entity.getClass().getName())));
|
return CompletableFuture.completedFuture(
|
||||||
|
new AccountHistoryTile(entity.getTimestamp(), new TextFlow(new Text("Unsupported entity: " + entity.getClass().getName())))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addEntitiesToHistory(List<Timestamped> entities, int requestedItems) {
|
private void addEntitiesToHistory(List<Timestamped> entities, int requestedItems) {
|
||||||
if (!itemsVBox.getChildren().isEmpty()) {
|
var futures = entities.stream().map(this::makeTile).toList();
|
||||||
itemsVBox.getChildren().add(new Separator(Orientation.HORIZONTAL));
|
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
||||||
}
|
.thenRun(() -> {
|
||||||
itemsVBox.getChildren().addAll(entities.stream()
|
List<AnchorPane> tiles = futures.stream().map(CompletableFuture::join)
|
||||||
.map(this::makeTile)
|
.map(tile -> {
|
||||||
.map(tile -> {
|
// Use this to scrunch content to the left.
|
||||||
// Use this to scrunch content to the left.
|
AnchorPane ap = new AnchorPane(tile);
|
||||||
AnchorPane ap = new AnchorPane(tile);
|
AnchorPane.setLeftAnchor(tile, 0.0);
|
||||||
AnchorPane.setLeftAnchor(tile, 0.0);
|
return ap;
|
||||||
return ap;
|
})
|
||||||
})
|
.toList();
|
||||||
.toList());
|
Platform.runLater(() -> {
|
||||||
if (entities.size() < requestedItems) {
|
if (!itemsVBox.getChildren().isEmpty()) {
|
||||||
canLoadMore.set(false);
|
itemsVBox.getChildren().add(new Separator(Orientation.HORIZONTAL));
|
||||||
BorderPane endMarker = new BorderPane(new Label("This is the start of the history."));
|
}
|
||||||
endMarker.getStyleClass().addAll("large-font", "italic-text");
|
itemsVBox.getChildren().addAll(tiles);
|
||||||
itemsVBox.getChildren().add(endMarker);
|
if (entities.size() < requestedItems) {
|
||||||
}
|
canLoadMore.set(false);
|
||||||
if (!entities.isEmpty()) {
|
BorderPane endMarker = new BorderPane(new Label("This is the start of the history."));
|
||||||
lastTimestamp = entities.getLast().getTimestamp();
|
endMarker.getStyleClass().addAll("large-font", "italic-text");
|
||||||
}
|
itemsVBox.getChildren().add(endMarker);
|
||||||
|
}
|
||||||
|
if (!entities.isEmpty()) {
|
||||||
|
lastTimestamp = entities.getLast().getTimestamp();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue