Added transaction descriptions to account history tiles.

This commit is contained in:
Andrew Lalis 2024-06-03 20:42:58 -04:00
parent 4cf95dba85
commit 5ce2360f05
2 changed files with 56 additions and 37 deletions

View File

@ -179,7 +179,7 @@ public record JdbcAccountRepository(Connection conn, Path contentDir) implements
SELECT id, timestamp, 'BALANCE_RECORD' AS type, account_id
FROM balance_record
)
WHERE account_id = ? AND timestamp <= ?
WHERE account_id = ? AND timestamp < ?
ORDER BY timestamp DESC
LIMIT\s""" + maxResults;
try (var stmt = conn.prepareStatement(query)) {

View File

@ -3,12 +3,10 @@ package com.andrewlalis.perfin.view.component;
import com.andrewlalis.perfin.control.TransactionsViewController;
import com.andrewlalis.perfin.data.AccountRepository;
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.DateUtil;
import com.andrewlalis.perfin.model.AccountEntry;
import com.andrewlalis.perfin.model.BalanceRecord;
import com.andrewlalis.perfin.model.Profile;
import com.andrewlalis.perfin.model.Timestamped;
import com.andrewlalis.perfin.model.*;
import com.andrewlalis.perfin.model.history.HistoryTextItem;
import com.andrewlalis.perfin.view.BindingUtil;
import javafx.application.Platform;
@ -27,6 +25,8 @@ import javafx.scene.text.TextFlow;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import static com.andrewlalis.perfin.PerfinApp.router;
@ -60,7 +60,7 @@ public class AccountHistoryView extends ScrollPane {
int maxItems = initialItemsToLoadProperty.get();
DataSource ds = Profile.getCurrent().dataSource();
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() {
@ -91,48 +91,65 @@ public class AccountHistoryView extends ScrollPane {
return lastTimestamp;
}
private Node makeTile(Timestamped entity) {
private CompletableFuture<Node> makeTile(Timestamped entity) {
switch (entity) {
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 -> {
Hyperlink txLink = new Hyperlink("Transaction #" + ae.getTransactionId());
txLink.setOnAction(event -> router.navigate("transactions", new TransactionsViewController.RouteContext(ae.getTransactionId())));
String descriptionFormat = ae.getType() == AccountEntry.Type.CREDIT
? "credited %s from this account."
: "debited %s to this account.";
String description = descriptionFormat.formatted(CurrencyUtil.formatMoney(ae.getMoneyValue()));
TextFlow textFlow = new TextFlow(txLink, new Text(description));
return new AccountHistoryTile(ae.getTimestamp(), textFlow);
? "credited %s from this account"
: "debited %s to this account";
final String description = descriptionFormat.formatted(CurrencyUtil.formatMoney(ae.getMoneyValue()));
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 -> {
Hyperlink brLink = new Hyperlink("Balance Record #" + br.id);
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,
new Text("added with a value of %s.".formatted(CurrencyUtil.formatMoney(br.getMoneyAmount())))
));
)));
}
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) {
if (!itemsVBox.getChildren().isEmpty()) {
itemsVBox.getChildren().add(new Separator(Orientation.HORIZONTAL));
}
itemsVBox.getChildren().addAll(entities.stream()
.map(this::makeTile)
var futures = entities.stream().map(this::makeTile).toList();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRun(() -> {
List<AnchorPane> tiles = futures.stream().map(CompletableFuture::join)
.map(tile -> {
// Use this to scrunch content to the left.
AnchorPane ap = new AnchorPane(tile);
AnchorPane.setLeftAnchor(tile, 0.0);
return ap;
})
.toList());
.toList();
Platform.runLater(() -> {
if (!itemsVBox.getChildren().isEmpty()) {
itemsVBox.getChildren().add(new Separator(Orientation.HORIZONTAL));
}
itemsVBox.getChildren().addAll(tiles);
if (entities.size() < requestedItems) {
canLoadMore.set(false);
BorderPane endMarker = new BorderPane(new Label("This is the start of the history."));
@ -142,5 +159,7 @@ public class AccountHistoryView extends ScrollPane {
if (!entities.isEmpty()) {
lastTimestamp = entities.getLast().getTimestamp();
}
});
});
}
}