From f23d2c85a90bdbcc11831e41d269025528b25fe6 Mon Sep 17 00:00:00 2001 From: andrewlalis Date: Wed, 10 Jul 2024 17:05:02 -0400 Subject: [PATCH] Added updates to use and show asset value of brokerage accounts. --- .../perfin/control/AccountViewController.java | 48 +++++++++++++---- .../control/BalanceRecordViewController.java | 2 + .../CreateBalanceRecordController.java | 43 ++++++++++----- .../perfin/data/AccountRepository.java | 11 ++-- .../andrewlalis/perfin/data/DataSource.java | 8 +-- .../data/impl/JdbcAccountRepository.java | 13 ++++- .../view/component/AccountHistoryView.java | 6 ++- .../view/component/AccountSelectionBox.java | 2 +- .../perfin/view/component/AccountTile.java | 29 +++++++++- .../view/component/module/AccountsModule.java | 29 +++++++++- src/main/resources/account-view.fxml | 54 +++++++++++-------- src/main/resources/balance-record-view.fxml | 3 ++ 12 files changed, 189 insertions(+), 59 deletions(-) diff --git a/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java b/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java index fc16078..9427669 100644 --- a/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java +++ b/src/main/java/com/andrewlalis/perfin/control/AccountViewController.java @@ -4,9 +4,7 @@ import com.andrewlalis.javafx_scene_router.RouteSelectionListener; import com.andrewlalis.perfin.data.AccountRepository; import com.andrewlalis.perfin.data.util.CurrencyUtil; import com.andrewlalis.perfin.data.util.DateUtil; -import com.andrewlalis.perfin.model.Account; -import com.andrewlalis.perfin.model.MoneyValue; -import com.andrewlalis.perfin.model.Profile; +import com.andrewlalis.perfin.model.*; import com.andrewlalis.perfin.view.BindingUtil; import com.andrewlalis.perfin.view.component.AccountHistoryView; import com.andrewlalis.perfin.view.component.PropertiesPane; @@ -14,7 +12,10 @@ 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.property.*; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; import javafx.scene.control.Button; @@ -23,7 +24,10 @@ import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.text.Text; -import java.time.*; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZoneOffset; import static com.andrewlalis.perfin.PerfinApp.router; @@ -31,6 +35,7 @@ public class AccountViewController implements RouteSelectionListener { private final ObjectProperty accountProperty = new SimpleObjectProperty<>(null); private final ObservableValue accountArchived = accountProperty.map(a -> a != null && a.isArchived()); private final StringProperty balanceTextProperty = new SimpleStringProperty(null); + private final StringProperty assetValueTextProperty = new SimpleStringProperty(null); @FXML public Label titleLabel; @FXML public Label accountNameLabel; @@ -38,6 +43,8 @@ public class AccountViewController implements RouteSelectionListener { @FXML public Label accountCurrencyLabel; @FXML public Label accountCreatedAtLabel; @FXML public Label accountBalanceLabel; + @FXML public PropertiesPane assetValuePane; + @FXML public Label latestAssetsValueLabel; @FXML public PropertiesPane descriptionPane; @FXML public Text accountDescriptionText; @@ -58,14 +65,23 @@ public class AccountViewController implements RouteSelectionListener { var hasDescription = accountProperty.map(a -> a.getDescription() != null); BindingUtil.bindManagedAndVisible(descriptionPane, hasDescription); accountBalanceLabel.textProperty().bind(balanceTextProperty); + var isBrokerageAccount = accountProperty.map(a -> a.getType() == AccountType.BROKERAGE); + BindingUtil.bindManagedAndVisible(assetValuePane, isBrokerageAccount); + latestAssetsValueLabel.textProperty().bind(assetValueTextProperty); actionsBox.getChildren().forEach(node -> { Button button = (Button) node; - ObservableValue buttonActive = accountArchived; + ObservableValue buttonDisabled = accountArchived; if (button.getText().equalsIgnoreCase("Unarchive")) { - buttonActive = BooleanExpression.booleanExpression(buttonActive).not(); + buttonDisabled = BooleanExpression.booleanExpression(buttonDisabled).not(); } - button.disableProperty().bind(buttonActive); + if (button.getText().equalsIgnoreCase("Record Asset Value")) { + buttonDisabled = BooleanExpression.booleanExpression( + accountProperty.map(Account::getType) + .map(t -> !t.equals(AccountType.BROKERAGE)) + ).or(BooleanExpression.booleanExpression(accountArchived)); + } + button.disableProperty().bind(buttonDisabled); button.managedProperty().bind(button.visibleProperty()); button.visibleProperty().bind(button.disableProperty().not()); }); @@ -81,7 +97,7 @@ public class AccountViewController implements RouteSelectionListener { .toInstant(); Profile.getCurrent().dataSource().mapRepoAsync( AccountRepository.class, - repo -> repo.deriveBalance(getAccount().id, timestamp) + repo -> repo.deriveCashBalance(getAccount().id, timestamp) ).thenAccept(balance -> Platform.runLater(() -> { String msg = String.format( "Your balance as of %s is %s, according to Perfin's data.", @@ -97,12 +113,20 @@ public class AccountViewController implements RouteSelectionListener { public void onRouteSelected(Object context) { accountHistory.clear(); balanceTextProperty.set(null); + assetValueTextProperty.set(null); if (context instanceof Account account) { this.accountProperty.set(account); accountHistory.setAccountId(account.id); accountHistory.loadMoreHistory(); Profile.getCurrent().dataSource().getAccountBalanceText(account) .thenAccept(s -> Platform.runLater(() -> balanceTextProperty.set(s))); + if (account.getType() == AccountType.BROKERAGE) { + Profile.getCurrent().dataSource().mapRepoAsync( + AccountRepository.class, + repo -> repo.getNearestAssetValue(account.id) + ).thenApply(value -> CurrencyUtil.formatMoney(new MoneyValue(value, account.getCurrency()))) + .thenAccept(text -> Platform.runLater(() -> assetValueTextProperty.set(text))); + } } } @@ -112,7 +136,11 @@ public class AccountViewController implements RouteSelectionListener { } @FXML public void goToCreateBalanceRecord() { - router.navigate("create-balance-record", getAccount()); + router.navigate("create-balance-record", new CreateBalanceRecordController.RouteContext(getAccount(), BalanceRecordType.CASH)); + } + + @FXML public void goToCreateAssetRecord() { + router.navigate("create-balance-record", new CreateBalanceRecordController.RouteContext(getAccount(), BalanceRecordType.ASSETS)); } @FXML diff --git a/src/main/java/com/andrewlalis/perfin/control/BalanceRecordViewController.java b/src/main/java/com/andrewlalis/perfin/control/BalanceRecordViewController.java index 9e2c4d4..5810b9e 100644 --- a/src/main/java/com/andrewlalis/perfin/control/BalanceRecordViewController.java +++ b/src/main/java/com/andrewlalis/perfin/control/BalanceRecordViewController.java @@ -24,6 +24,7 @@ public class BalanceRecordViewController implements RouteSelectionListener { @FXML public Label titleLabel; + @FXML public Label typeLabel; @FXML public Label timestampLabel; @FXML public Label balanceLabel; @FXML public Label currencyLabel; @@ -38,6 +39,7 @@ public class BalanceRecordViewController implements RouteSelectionListener { this.balanceRecord = (BalanceRecord) context; if (balanceRecord == null) return; titleLabel.setText("Balance Record #" + balanceRecord.id); + typeLabel.setText(balanceRecord.getType().toString()); timestampLabel.setText(DateUtil.formatUTCAsLocalWithZone(balanceRecord.getTimestamp())); balanceLabel.setText(CurrencyUtil.formatMoney(balanceRecord.getMoneyAmount())); currencyLabel.setText(balanceRecord.getCurrency().getDisplayName()); diff --git a/src/main/java/com/andrewlalis/perfin/control/CreateBalanceRecordController.java b/src/main/java/com/andrewlalis/perfin/control/CreateBalanceRecordController.java index 53f372d..17e9a46 100644 --- a/src/main/java/com/andrewlalis/perfin/control/CreateBalanceRecordController.java +++ b/src/main/java/com/andrewlalis/perfin/control/CreateBalanceRecordController.java @@ -35,6 +35,8 @@ import static com.andrewlalis.perfin.PerfinApp.router; * account. */ public class CreateBalanceRecordController implements RouteSelectionListener { + public record RouteContext (Account account, BalanceRecordType type) {} + @FXML public TextField timestampField; @FXML public TextField balanceField; @FXML public Label balanceWarningLabel; @@ -44,6 +46,7 @@ public class CreateBalanceRecordController implements RouteSelectionListener { @FXML public Button saveButton; private Account account; + private BalanceRecordType type = BalanceRecordType.CASH; @FXML public void initialize() { var timestampValid = new ValidationApplier<>((ValidationFunction) input -> { @@ -62,7 +65,7 @@ public class CreateBalanceRecordController implements RouteSelectionListener { balanceWarningLabel.managedProperty().bind(balanceWarningLabel.visibleProperty()); balanceWarningLabel.visibleProperty().set(false); balanceField.textProperty().addListener((observable, oldValue, newValue) -> { - if (!balanceValidator.validate(newValue).isValid() || !timestampValid.get()) { + if (!balanceValidator.validate(newValue).isValid() || !timestampValid.get() || type != BalanceRecordType.CASH) { balanceWarningLabel.visibleProperty().set(false); return; } @@ -70,7 +73,7 @@ public class CreateBalanceRecordController implements RouteSelectionListener { LocalDateTime localTimestamp = LocalDateTime.parse(timestampField.getText(), DateUtil.DEFAULT_DATETIME_FORMAT); LocalDateTime utcTimestamp = DateUtil.localToUTC(localTimestamp); Profile.getCurrent().dataSource().useRepoAsync(AccountRepository.class, repo -> { - BigDecimal derivedBalance = repo.deriveBalance(account.id, utcTimestamp.toInstant(ZoneOffset.UTC)); + BigDecimal derivedBalance = repo.deriveCashBalance(account.id, utcTimestamp.toInstant(ZoneOffset.UTC)); boolean balancesMatch = reportedBalance.setScale(derivedBalance.scale(), RoundingMode.HALF_UP).equals(derivedBalance); Platform.runLater(() -> balanceWarningLabel.visibleProperty().set(!balancesMatch)); }); @@ -82,14 +85,19 @@ public class CreateBalanceRecordController implements RouteSelectionListener { @Override public void onRouteSelected(Object context) { - this.account = (Account) context; + RouteContext ctx = (RouteContext) context; + this.account = ctx.account(); + this.type = ctx.type(); timestampField.setText(LocalDateTime.now().format(DateUtil.DEFAULT_DATETIME_FORMAT)); - Profile.getCurrent().dataSource().useRepoAsync(AccountRepository.class, repo -> { - BigDecimal value = repo.deriveCurrentBalance(account.id); - Platform.runLater(() -> balanceField.setText( - CurrencyUtil.formatMoneyAsBasicNumber(new MoneyValue(value, account.getCurrency())) - )); - }); + balanceField.setText(null); + if (ctx.type() == BalanceRecordType.CASH) { + Profile.getCurrent().dataSource().useRepoAsync(AccountRepository.class, repo -> { + BigDecimal value = repo.deriveCurrentCashBalance(account.id); + Platform.runLater(() -> balanceField.setText( + CurrencyUtil.formatMoneyAsBasicNumber(new MoneyValue(value, account.getCurrency())) + )); + }); + } attachmentSelectionArea.clear(); } @@ -97,17 +105,26 @@ public class CreateBalanceRecordController implements RouteSelectionListener { LocalDateTime localTimestamp = LocalDateTime.parse(timestampField.getText(), DateUtil.DEFAULT_DATETIME_FORMAT); BigDecimal reportedBalance = new BigDecimal(balanceField.getText()); - boolean confirm = Popups.confirm(timestampField, "Are you sure that you want to record the balance of account\n%s\nas %s,\nas of %s?".formatted( + String valueNoun = switch (type) { + case CASH -> "cash balance"; + case ASSETS -> "asset value"; + }; + + boolean confirm = Popups.confirm(timestampField, "Are you sure that you want to record the %s of account\n%s\nas %s,\nas of %s?".formatted( + valueNoun, account.getShortName(), CurrencyUtil.formatMoneyWithCurrencyPrefix(new MoneyValue(reportedBalance, account.getCurrency())), localTimestamp.atZone(ZoneId.systemDefault()).format(DateUtil.DEFAULT_DATETIME_FORMAT_WITH_ZONE) )); - if (confirm && confirmIfInconsistentBalance(reportedBalance, DateUtil.localToUTC(localTimestamp))) { + if ( + confirm && + (type != BalanceRecordType.CASH || confirmIfInconsistentBalance(reportedBalance, DateUtil.localToUTC(localTimestamp))) + ) { Profile.getCurrent().dataSource().useRepo(BalanceRecordRepository.class, repo -> { repo.insert( DateUtil.localToUTC(localTimestamp), account.id, - BalanceRecordType.CASH, + type, reportedBalance, account.getCurrency(), attachmentSelectionArea.getSelectedPaths() @@ -124,7 +141,7 @@ public class CreateBalanceRecordController implements RouteSelectionListener { private boolean confirmIfInconsistentBalance(BigDecimal reportedBalance, LocalDateTime utcTimestamp) { BigDecimal currentDerivedBalance = Profile.getCurrent().dataSource().mapRepo( AccountRepository.class, - repo -> repo.deriveBalance(account.id, utcTimestamp.toInstant(ZoneOffset.UTC)) + repo -> repo.deriveCashBalance(account.id, utcTimestamp.toInstant(ZoneOffset.UTC)) ); if (!reportedBalance.setScale(currentDerivedBalance.scale(), RoundingMode.HALF_UP).equals(currentDerivedBalance)) { String msg = "The balance you reported (%s) doesn't match the balance that Perfin derived from your account's transactions (%s). It's encouraged to go back and add any missing transactions first, but you may proceed now if you understand the consequences of an inconsistent account balance history.\n\nAre you absolutely sure you want to create this balance record?".formatted( diff --git a/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java b/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java index c936f47..4562a1b 100644 --- a/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java +++ b/src/main/java/com/andrewlalis/perfin/data/AccountRepository.java @@ -28,10 +28,15 @@ public interface AccountRepository extends Repository, AutoCloseable { void archive(long accountId); void unarchive(long accountId); - BigDecimal deriveBalance(long accountId, Instant timestamp); - default BigDecimal deriveCurrentBalance(long accountId) { - return deriveBalance(accountId, Instant.now(Clock.systemUTC())); + BigDecimal deriveCashBalance(long accountId, Instant timestamp); + default BigDecimal deriveCurrentCashBalance(long accountId) { + return deriveCashBalance(accountId, Instant.now(Clock.systemUTC())); } + BigDecimal getNearestAssetValue(long accountId, Instant timestamp); + default BigDecimal getNearestAssetValue(long accountId) { + return getNearestAssetValue(accountId, Instant.now(Clock.systemUTC())); + } + Set findAllUsedCurrencies(); List findEventsBefore(long accountId, LocalDateTime utcTimestamp, int maxResults); } diff --git a/src/main/java/com/andrewlalis/perfin/data/DataSource.java b/src/main/java/com/andrewlalis/perfin/data/DataSource.java index d542728..bd792e4 100644 --- a/src/main/java/com/andrewlalis/perfin/data/DataSource.java +++ b/src/main/java/com/andrewlalis/perfin/data/DataSource.java @@ -104,7 +104,7 @@ public interface DataSource { default CompletableFuture getAccountBalanceText(Account account) { CompletableFuture cf = new CompletableFuture<>(); mapRepoAsync(AccountRepository.class, repo -> { - BigDecimal balance = repo.deriveCurrentBalance(account.id); + BigDecimal balance = repo.deriveCurrentCashBalance(account.id); MoneyValue money = new MoneyValue(balance, account.getCurrency()); return CurrencyUtil.formatMoney(money); }).thenAccept(s -> Platform.runLater(() -> cf.complete(s))); @@ -123,9 +123,11 @@ public interface DataSource { Map totals = new HashMap<>(); for (var account : accounts) { BigDecimal currencyTotal = totals.computeIfAbsent(account.getCurrency(), c -> BigDecimal.ZERO); - BigDecimal accountBalance = repo.deriveBalance(account.id, timestamp); + BigDecimal accountBalance = repo.deriveCashBalance(account.id, timestamp); + BigDecimal accountAssetsValue = repo.getNearestAssetValue(account.id, timestamp); if (account.getType() == AccountType.CREDIT_CARD) accountBalance = accountBalance.negate(); - totals.put(account.getCurrency(), currencyTotal.add(accountBalance)); + BigDecimal accountTotal = accountBalance.add(accountAssetsValue); + totals.put(account.getCurrency(), currencyTotal.add(accountTotal)); } List values = new ArrayList<>(totals.size()); for (var entry : totals.entrySet()) { diff --git a/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java b/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java index 3da41e5..a451eb4 100644 --- a/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java +++ b/src/main/java/com/andrewlalis/perfin/data/impl/JdbcAccountRepository.java @@ -114,7 +114,7 @@ public record JdbcAccountRepository(Connection conn, Path contentDir) implements } @Override - public BigDecimal deriveBalance(long accountId, Instant timestamp) { + public BigDecimal deriveCashBalance(long accountId, Instant timestamp) { // First find the account itself, since its properties influence the balance. Account account = findById(accountId).orElse(null); if (account == null) throw new EntityNotFoundException(Account.class, accountId); @@ -152,6 +152,15 @@ public record JdbcAccountRepository(Connection conn, Path contentDir) implements } } + @Override + public BigDecimal getNearestAssetValue(long accountId, Instant timestamp) { + LocalDateTime utcTimestamp = timestamp.atZone(ZoneOffset.UTC).toLocalDateTime(); + BalanceRecordRepository balanceRecordRepo = new JdbcBalanceRecordRepository(conn, contentDir); + Optional mostRecentRecord = balanceRecordRepo.findClosestBefore(accountId, BalanceRecordType.ASSETS, utcTimestamp); + if (mostRecentRecord.isEmpty()) return BigDecimal.ZERO; + return mostRecentRecord.get().getBalance(); + } + @Override public Set findAllUsedCurrencies() { return new HashSet<>(DbUtil.findAll( @@ -177,7 +186,7 @@ public record JdbcAccountRepository(Connection conn, Path contentDir) implements LEFT JOIN history_account ha ON history_item.history_id = ha.history_id UNION ALL SELECT id, timestamp, 'BALANCE_RECORD' AS type, account_id - FROM balance_record WHERE type = 'CASH' + FROM balance_record ) WHERE account_id = ? AND timestamp < ? ORDER BY timestamp DESC diff --git a/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryView.java b/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryView.java index df3669a..c34f12a 100644 --- a/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryView.java +++ b/src/main/java/com/andrewlalis/perfin/view/component/AccountHistoryView.java @@ -120,9 +120,13 @@ public class AccountHistoryView extends ScrollPane { case BalanceRecord br -> { Hyperlink brLink = new Hyperlink("Balance Record #" + br.id); brLink.setOnAction(event -> router.navigate("balance-record", br)); + String phrase = switch(br.getType()) { + case CASH -> "a cash value"; + case ASSETS -> "an asset value"; + }; return CompletableFuture.completedFuture(new AccountHistoryTile(br.getTimestamp(), new TextFlow( brLink, - new Text("added with a value of %s.".formatted(CurrencyUtil.formatMoney(br.getMoneyAmount()))) + new Text("added with %s of %s.".formatted(phrase, CurrencyUtil.formatMoney(br.getMoneyAmount()))) ))); } default -> { diff --git a/src/main/java/com/andrewlalis/perfin/view/component/AccountSelectionBox.java b/src/main/java/com/andrewlalis/perfin/view/component/AccountSelectionBox.java index eacddea..32ecba1 100644 --- a/src/main/java/com/andrewlalis/perfin/view/component/AccountSelectionBox.java +++ b/src/main/java/com/andrewlalis/perfin/view/component/AccountSelectionBox.java @@ -113,7 +113,7 @@ public class AccountSelectionBox extends ComboBox { nameLabel.setText(item.getName() + " (" + item.getAccountNumberSuffix() + ")"); if (showBalanceProp.get()) { Profile.getCurrent().dataSource().useRepoAsync(AccountRepository.class, repo -> { - BigDecimal balance = repo.deriveCurrentBalance(item.id); + BigDecimal balance = repo.deriveCurrentCashBalance(item.id); Platform.runLater(() -> { balanceLabel.setText(CurrencyUtil.formatMoney(new MoneyValue(balance, item.getCurrency()))); balanceLabel.setVisible(true); diff --git a/src/main/java/com/andrewlalis/perfin/view/component/AccountTile.java b/src/main/java/com/andrewlalis/perfin/view/component/AccountTile.java index a21690a..9d82a1b 100644 --- a/src/main/java/com/andrewlalis/perfin/view/component/AccountTile.java +++ b/src/main/java/com/andrewlalis/perfin/view/component/AccountTile.java @@ -17,6 +17,7 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import java.math.BigDecimal; +import java.time.Instant; import java.util.Map; import static com.andrewlalis.perfin.PerfinApp.router; @@ -83,7 +84,7 @@ public class AccountTile extends BorderPane { balanceLabel.getStyleClass().addAll("mono-font"); balanceLabel.setDisable(true); Profile.getCurrent().dataSource().useRepoAsync(AccountRepository.class, repo -> { - BigDecimal balance = repo.deriveCurrentBalance(account.id); + BigDecimal balance = repo.deriveCurrentCashBalance(account.id); String text = CurrencyUtil.formatMoney(new MoneyValue(balance, account.getCurrency())); Platform.runLater(() -> { balanceLabel.setText(text); @@ -104,6 +105,32 @@ public class AccountTile extends BorderPane { newPropertyLabel("Current Balance"), balanceLabel ); + + if (account.getType() == AccountType.BROKERAGE) { + Label assetValueLabel = new Label("Computing assets value..."); + assetValueLabel.getStyleClass().addAll("mono-font"); + assetValueLabel.setDisable(true); + + Profile.getCurrent().dataSource().useRepoAsync(AccountRepository.class, repo -> { + BigDecimal assetValue = repo.getNearestAssetValue(account.id); + String text = CurrencyUtil.formatMoney(new MoneyValue(assetValue, account.getCurrency())); + Platform.runLater(() -> { + assetValueLabel.setText(text); + if (account.getType().areDebitsPositive() && assetValue.compareTo(BigDecimal.ZERO) < 0) { + assetValueLabel.getStyleClass().add("negative-color-text-fill"); + } else if (!account.getType().areDebitsPositive() && assetValue.compareTo(BigDecimal.ZERO) < 0) { + assetValueLabel.getStyleClass().add("positive-color-text-fill"); + } + assetValueLabel.setDisable(false); + }); + }); + + propertiesPane.getChildren().addAll( + newPropertyLabel("Latest Assets Value"), + assetValueLabel + ); + } + return propertiesPane; } diff --git a/src/main/java/com/andrewlalis/perfin/view/component/module/AccountsModule.java b/src/main/java/com/andrewlalis/perfin/view/component/module/AccountsModule.java index 54d020c..e36261a 100644 --- a/src/main/java/com/andrewlalis/perfin/view/component/module/AccountsModule.java +++ b/src/main/java/com/andrewlalis/perfin/view/component/module/AccountsModule.java @@ -3,6 +3,7 @@ package com.andrewlalis.perfin.view.component.module; import com.andrewlalis.perfin.data.AccountRepository; import com.andrewlalis.perfin.data.util.CurrencyUtil; import com.andrewlalis.perfin.model.Account; +import com.andrewlalis.perfin.model.AccountType; import com.andrewlalis.perfin.model.MoneyValue; import com.andrewlalis.perfin.model.Profile; import com.andrewlalis.perfin.view.component.AccountTile; @@ -91,13 +92,17 @@ public class AccountsModule extends DashboardModule { Label typeLabel = new Label(account.getType().toString()); typeLabel.getStyleClass().add("bold-text"); typeLabel.setStyle("-fx-text-fill: " + AccountTile.ACCOUNT_TYPE_COLORS.get(account.getType())); + + VBox rightSideVBox = new VBox(); + rightSideVBox.getStyleClass().addAll("std-spacing"); Label balanceLabel = new Label("Computing balance..."); balanceLabel.getStyleClass().addAll("mono-font"); balanceLabel.setDisable(true); + rightSideVBox.getChildren().add(balanceLabel); Profile.getCurrent().dataSource().mapRepoAsync( AccountRepository.class, - repo -> repo.deriveCurrentBalance(account.id) + repo -> repo.deriveCurrentCashBalance(account.id) ).thenAccept(bal -> Platform.runLater(() -> { String text = CurrencyUtil.formatMoneyWithCurrencyPrefix(new MoneyValue(bal, account.getCurrency())); balanceLabel.setText(text); @@ -109,9 +114,29 @@ public class AccountsModule extends DashboardModule { balanceLabel.setDisable(false); })); + if (account.getType() == AccountType.BROKERAGE) { + Label assetValueLabel = new Label("Computing assets value..."); + assetValueLabel.getStyleClass().addAll("mono-font"); + assetValueLabel.setDisable(true); + rightSideVBox.getChildren().add(assetValueLabel); + Profile.getCurrent().dataSource().mapRepoAsync( + AccountRepository.class, + repo -> repo.getNearestAssetValue(account.id) + ).thenAccept(value -> Platform.runLater(() -> { + String text = CurrencyUtil.formatMoneyWithCurrencyPrefix(new MoneyValue(value, account.getCurrency())); + assetValueLabel.setText(text + " in assets"); + if (account.getType().areDebitsPositive() && value.compareTo(BigDecimal.ZERO) < 0) { + assetValueLabel.getStyleClass().add("negative-color-text-fill"); + } else if (!account.getType().areDebitsPositive() && value.compareTo(BigDecimal.ZERO) < 0) { + assetValueLabel.getStyleClass().add("positive-color-text-fill"); + } + assetValueLabel.setDisable(false); + })); + } + VBox contentBox = new VBox(nameLabel, numberLabel, typeLabel); borderPane.setCenter(contentBox); - borderPane.setRight(balanceLabel); + borderPane.setRight(rightSideVBox); return borderPane; } } diff --git a/src/main/resources/account-view.fxml b/src/main/resources/account-view.fxml index f99b98a..fc372cd 100644 --- a/src/main/resources/account-view.fxml +++ b/src/main/resources/account-view.fxml @@ -4,7 +4,8 @@ - + +
- - - - - + + + - + + + + @@ -51,6 +58,7 @@