Added historical balance checker and cleaned up actions layout on account page.

This commit is contained in:
Andrew Lalis 2024-02-07 09:05:50 -05:00
parent 104de66a66
commit 6bafd06fc0
3 changed files with 90 additions and 41 deletions

View File

@ -2,17 +2,25 @@ package com.andrewlalis.perfin.control;
import com.andrewlalis.javafx_scene_router.RouteSelectionListener; import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
import com.andrewlalis.perfin.data.AccountRepository; import com.andrewlalis.perfin.data.AccountRepository;
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.Account; import com.andrewlalis.perfin.model.Account;
import com.andrewlalis.perfin.model.MoneyValue;
import com.andrewlalis.perfin.model.Profile; import com.andrewlalis.perfin.model.Profile;
import com.andrewlalis.perfin.view.component.AccountHistoryView; import com.andrewlalis.perfin.view.component.AccountHistoryView;
import com.andrewlalis.perfin.view.component.validation.ValidationApplier;
import com.andrewlalis.perfin.view.component.validation.validators.PredicateValidator;
import javafx.application.Platform;
import javafx.beans.binding.BooleanExpression; import javafx.beans.binding.BooleanExpression;
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.Button; import javafx.scene.control.Button;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.VBox; import javafx.scene.layout.HBox;
import java.time.*;
import static com.andrewlalis.perfin.PerfinApp.router; import static com.andrewlalis.perfin.PerfinApp.router;
@ -30,10 +38,13 @@ public class AccountViewController implements RouteSelectionListener {
@FXML public AccountHistoryView accountHistory; @FXML public AccountHistoryView accountHistory;
@FXML public VBox actionsVBox; @FXML public HBox actionsBox;
@FXML public DatePicker balanceCheckerDatePicker;
@FXML public Button balanceCheckerButton;
@FXML public void initialize() { @FXML public void initialize() {
actionsVBox.getChildren().forEach(node -> { actionsBox.getChildren().forEach(node -> {
Button button = (Button) node; Button button = (Button) node;
BooleanExpression buttonActive = accountArchivedProperty; BooleanExpression buttonActive = accountArchivedProperty;
if (button.getText().equalsIgnoreCase("Unarchive")) { if (button.getText().equalsIgnoreCase("Unarchive")) {
@ -43,6 +54,28 @@ public class AccountViewController implements RouteSelectionListener {
button.managedProperty().bind(button.visibleProperty()); button.managedProperty().bind(button.visibleProperty());
button.visibleProperty().bind(button.disableProperty().not()); button.visibleProperty().bind(button.disableProperty().not());
}); });
var datePickerValid = new ValidationApplier<>(new PredicateValidator<LocalDate>()
.addPredicate(date -> date.isBefore(LocalDate.now()), "Date must be in the past.")
).attach(balanceCheckerDatePicker, balanceCheckerDatePicker.valueProperty());
balanceCheckerButton.disableProperty().bind(datePickerValid.not());
balanceCheckerButton.setOnAction(event -> {
LocalDate date = balanceCheckerDatePicker.getValue();
final Instant timestamp = date.atStartOfDay(ZoneId.systemDefault())
.withZoneSameInstant(ZoneOffset.UTC)
.toInstant();
Profile.getCurrent().dataSource().mapRepoAsync(
AccountRepository.class,
repo -> repo.deriveBalance(account.id, timestamp)
).thenAccept(balance -> Platform.runLater(() -> {
String msg = String.format(
"Your balance as of %s is %s, according to Perfin's data.",
date,
CurrencyUtil.formatMoney(new MoneyValue(balance, account.getCurrency()))
);
Popups.message(balanceCheckerButton, msg);
}));
});
} }
@Override @Override

View File

@ -81,6 +81,7 @@ public class StyledText extends VBox {
idx = 0; idx = 0;
currentRun.setLength(0); currentRun.setLength(0);
currentParagraph = new TextFlow(); currentParagraph = new TextFlow();
currentParagraph.setStyle("-fx-text-fill: inherit;");
while (idx < text.length()) { while (idx < text.length()) {
if (text.startsWith("**", idx)) { if (text.startsWith("**", idx)) {
@ -118,6 +119,7 @@ public class StyledText extends VBox {
int endIdx = text.indexOf(marker, idx + marker.length()); int endIdx = text.indexOf(marker, idx + marker.length());
Text textItem = new Text(text.substring(idx + marker.length(), endIdx)); Text textItem = new Text(text.substring(idx + marker.length(), endIdx));
textItem.getStyleClass().add(styleClass); textItem.getStyleClass().add(styleClass);
textItem.setStyle("-fx-text-fill: inherit;");
currentParagraph.getChildren().add(textItem); currentParagraph.getChildren().add(textItem);
idx = endIdx + marker.length(); idx = endIdx + marker.length();
} }
@ -183,6 +185,7 @@ public class StyledText extends VBox {
if (!currentParagraph.getChildren().isEmpty()) { if (!currentParagraph.getChildren().isEmpty()) {
flows.add(currentParagraph); flows.add(currentParagraph);
currentParagraph = new TextFlow(); currentParagraph = new TextFlow();
currentParagraph.setStyle("-fx-text-fill: inherit;");
} }
} }
} }

View File

@ -5,6 +5,8 @@
<?import javafx.scene.control.*?> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<?import javafx.scene.text.Text?> <?import javafx.scene.text.Text?>
<?import com.andrewlalis.perfin.view.component.StyledText?>
<?import javafx.scene.text.TextFlow?>
<BorderPane <BorderPane
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml" xmlns:fx="http://javafx.com/fxml"
@ -16,10 +18,7 @@
<center> <center>
<VBox> <VBox>
<!-- Main account properties and actions --> <!-- Main account properties and actions -->
<BorderPane> <FlowPane styleClass="std-padding,std-spacing">
<center>
<VBox styleClass="std-padding,std-spacing">
<FlowPane>
<!-- Main account properties. --> <!-- Main account properties. -->
<PropertiesPane vgap="5" hgap="5"> <PropertiesPane vgap="5" hgap="5">
<Label text="Name" styleClass="bold-text"/> <Label text="Name" styleClass="bold-text"/>
@ -44,21 +43,35 @@
<Label fx:id="accountBalanceLabel" styleClass="mono-font"/> <Label fx:id="accountBalanceLabel" styleClass="mono-font"/>
</PropertiesPane> </PropertiesPane>
</FlowPane> </FlowPane>
</VBox>
</center> <!-- Action buttons -->
<right> <HBox fx:id="actionsBox" styleClass="std-padding,std-spacing,small-font">
<Button text="Edit" onAction="#goToEditPage"/>
<Button text="Record Balance" onAction="#goToCreateBalanceRecord"/>
<Button text="Archive" onAction="#archiveAccount"/>
<Button text="Delete" onAction="#deleteAccount"/>
<Button text="Unarchive" onAction="#unarchiveAccount"/>
</HBox>
<!-- Historical Balance Checker -->
<VBox styleClass="std-padding,std-spacing"> <VBox styleClass="std-padding,std-spacing">
<Label text="Actions" styleClass="bold-text"/> <HBox styleClass="std-spacing">
<VBox fx:id="actionsVBox" styleClass="std-spacing"> <AnchorPane>
<Button text="Edit" onAction="#goToEditPage" maxWidth="Infinity"/> <Label text="Check Historical Balance" styleClass="bold-text" AnchorPane.topAnchor="0" AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0"/>
<Button text="Record Balance" onAction="#goToCreateBalanceRecord" maxWidth="Infinity"/> </AnchorPane>
<Button text="Archive" onAction="#archiveAccount" maxWidth="Infinity"/> <DatePicker fx:id="balanceCheckerDatePicker"/>
<Button text="Delete" onAction="#deleteAccount" maxWidth="Infinity"/> <Button text="Check" fx:id="balanceCheckerButton"/>
<Button text="Unarchive" onAction="#unarchiveAccount" maxWidth="Infinity"/> </HBox>
<TextFlow maxWidth="500">
<Text styleClass="small-font,secondary-color-fill">
Use this tool to check the balance of this account on any
given day in the past. Perfin will compute the balance
according to any balance records and transactions it finds
around that time.
</Text>
</TextFlow>
</VBox> </VBox>
</VBox>
</right>
</BorderPane>
<!-- Account history --> <!-- Account history -->
<VBox VBox.vgrow="ALWAYS"> <VBox VBox.vgrow="ALWAYS">