Add new "Brokerage" account type, and add more convenience for getting total assets.
This commit is contained in:
parent
d43c61d6ee
commit
5a339cbee6
|
@ -4,7 +4,6 @@ import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
|||
import com.andrewlalis.perfin.data.AccountRepository;
|
||||
import com.andrewlalis.perfin.data.util.CurrencyUtil;
|
||||
import com.andrewlalis.perfin.model.Account;
|
||||
import com.andrewlalis.perfin.model.MoneyValue;
|
||||
import com.andrewlalis.perfin.model.Profile;
|
||||
import com.andrewlalis.perfin.view.component.AccountTile;
|
||||
import javafx.application.Platform;
|
||||
|
@ -57,15 +56,9 @@ public class AccountsViewController implements RouteSelectionListener {
|
|||
.toList()
|
||||
));
|
||||
});
|
||||
// Compute grand totals!
|
||||
Thread.ofVirtual().start(() -> {
|
||||
var totals = profile.dataSource().getCombinedAccountBalances();
|
||||
StringBuilder sb = new StringBuilder("Totals: ");
|
||||
for (var entry : totals.entrySet()) {
|
||||
sb.append(CurrencyUtil.formatMoneyWithCurrencyPrefix(new MoneyValue(entry.getValue(), entry.getKey())));
|
||||
}
|
||||
Platform.runLater(() -> totalLabel.setText(sb.toString().strip()));
|
||||
});
|
||||
profile.dataSource().getCombinedAccountBalances()
|
||||
.thenApply(CurrencyUtil::formatMoneyValues)
|
||||
.thenAccept(s -> Platform.runLater(() -> totalLabel.setText("Totals: " + s)));
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
@ -86,6 +86,7 @@ public class EditAccountController implements RouteSelectionListener {
|
|||
accountTypeChoiceBox.getItems().add(AccountType.CHECKING);
|
||||
accountTypeChoiceBox.getItems().add(AccountType.SAVINGS);
|
||||
accountTypeChoiceBox.getItems().add(AccountType.CREDIT_CARD);
|
||||
accountTypeChoiceBox.getItems().add(AccountType.BROKERAGE);
|
||||
accountTypeChoiceBox.getSelectionModel().select(AccountType.CHECKING);
|
||||
|
||||
initialBalanceContent.visibleProperty().bind(creatingNewAccount);
|
||||
|
|
|
@ -106,19 +106,27 @@ public interface DataSource {
|
|||
return cf;
|
||||
}
|
||||
|
||||
default Map<Currency, BigDecimal> getCombinedAccountBalances() {
|
||||
try (var accountRepo = getAccountRepository()) {
|
||||
List<Account> accounts = accountRepo.findAll(PageRequest.unpaged()).items();
|
||||
/**
|
||||
* Gets a list of combined total assets for each currency that's tracked,
|
||||
* ordered with highest assets first.
|
||||
* @return A future that resolves to the list of amounts for each currency.
|
||||
*/
|
||||
default CompletableFuture<List<MoneyValue>> getCombinedAccountBalances() {
|
||||
return mapRepoAsync(AccountRepository.class, repo -> {
|
||||
List<Account> accounts = repo.findAll(PageRequest.unpaged()).items();
|
||||
Map<Currency, BigDecimal> totals = new HashMap<>();
|
||||
for (var account : accounts) {
|
||||
BigDecimal currencyTotal = totals.computeIfAbsent(account.getCurrency(), c -> BigDecimal.ZERO);
|
||||
BigDecimal accountBalance = accountRepo.deriveCurrentBalance(account.id);
|
||||
BigDecimal accountBalance = repo.deriveCurrentBalance(account.id);
|
||||
if (account.getType() == AccountType.CREDIT_CARD) accountBalance = accountBalance.negate();
|
||||
totals.put(account.getCurrency(), currencyTotal.add(accountBalance));
|
||||
}
|
||||
return totals;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
List<MoneyValue> values = new ArrayList<>(totals.size());
|
||||
for (var entry : totals.entrySet()) {
|
||||
values.add(new MoneyValue(entry.getValue(), entry.getKey()));
|
||||
}
|
||||
values.sort((m1, m2) -> m2.amount().compareTo(m1.amount()));
|
||||
return values;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -216,8 +216,8 @@ public record JdbcAccountRepository(Connection conn, Path contentDir) implements
|
|||
if (account == null) return;
|
||||
List<String> updateMessages = new ArrayList<>();
|
||||
if (account.getType() != type) {
|
||||
DbUtil.updateOne(conn, "UPDATE account SET account_type = ? WHERE id = ?", type, accountId);
|
||||
updateMessages.add(String.format("Updated account type from %s to %s.", account.getType().toString(), type.toString()));
|
||||
DbUtil.updateOne(conn, "UPDATE account SET account_type = ? WHERE id = ?", type.name(), accountId);
|
||||
updateMessages.add(String.format("Updated account type from %s to %s.", account.getType(), type));
|
||||
}
|
||||
if (!account.getAccountNumber().equals(accountNumber)) {
|
||||
DbUtil.updateOne(conn, "UPDATE account SET account_number = ? WHERE id = ?", accountNumber, accountId);
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.andrewlalis.perfin.model.MoneyValue;
|
|||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class CurrencyUtil {
|
||||
|
@ -26,4 +27,14 @@ public class CurrencyUtil {
|
|||
BigDecimal displayValue = money.amount().setScale(money.currency().getDefaultFractionDigits(), RoundingMode.HALF_UP);
|
||||
return displayValue.toString();
|
||||
}
|
||||
|
||||
public static String formatMoneyValues(List<MoneyValue> values) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
final int len = values.size();
|
||||
for (int i = 0; i < len; i++) {
|
||||
sb.append(formatMoneyWithCurrencyPrefix(values.get(i)));
|
||||
if (i < len - 1) sb.append(", ");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ package com.andrewlalis.perfin.model;
|
|||
public enum AccountType {
|
||||
CHECKING("Checking", true),
|
||||
SAVINGS("Savings", true),
|
||||
CREDIT_CARD("Credit Card", false);
|
||||
CREDIT_CARD("Credit Card", false),
|
||||
BROKERAGE("Brokerage", true);
|
||||
|
||||
private final String name;
|
||||
private final boolean debitsPositive;
|
||||
|
@ -24,14 +25,4 @@ public enum AccountType {
|
|||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static AccountType parse(String s) {
|
||||
s = s.strip().toUpperCase();
|
||||
return switch (s) {
|
||||
case "CHECKING" -> CHECKING;
|
||||
case "SAVINGS" -> SAVINGS;
|
||||
case "CREDIT CARD", "CREDITCARD" -> CREDIT_CARD;
|
||||
default -> throw new IllegalArgumentException("Invalid AccountType string: " + s);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,8 @@ public class AccountTile extends BorderPane {
|
|||
public static final Map<AccountType, String> ACCOUNT_TYPE_COLORS = Map.of(
|
||||
AccountType.CHECKING, "-fx-theme-account-type-checking",
|
||||
AccountType.SAVINGS, "-fx-theme-account-type-savings",
|
||||
AccountType.CREDIT_CARD, "-fx-theme-account-type-credit-card"
|
||||
AccountType.CREDIT_CARD, "-fx-theme-account-type-credit-card",
|
||||
AccountType.BROKERAGE, "-fx-theme-account-type-brokerage"
|
||||
);
|
||||
|
||||
public AccountTile(Account account) {
|
||||
|
|
|
@ -11,10 +11,7 @@ import javafx.scene.Node;
|
|||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.layout.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
|
@ -25,6 +22,7 @@ import static com.andrewlalis.perfin.PerfinApp.router;
|
|||
*/
|
||||
public class AccountsModule extends DashboardModule {
|
||||
private final VBox accountsVBox = new VBox();
|
||||
private final Label totalAssetsLabel = new Label();
|
||||
|
||||
public AccountsModule(Pane parent) {
|
||||
super(parent);
|
||||
|
@ -48,6 +46,16 @@ public class AccountsModule extends DashboardModule {
|
|||
refreshButton
|
||||
));
|
||||
this.getChildren().add(scrollPane);
|
||||
|
||||
HBox footer = new HBox();
|
||||
footer.getStyleClass().addAll("std-padding", "std-spacing");
|
||||
|
||||
totalAssetsLabel.getStyleClass().addAll("mono-font");
|
||||
HBox totalAssetsBox = new HBox(new Label("Total Tracked Assets: "), totalAssetsLabel);
|
||||
totalAssetsBox.getStyleClass().addAll("std-spacing");
|
||||
footer.getChildren().add(totalAssetsBox);
|
||||
|
||||
this.getChildren().add(footer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -61,6 +69,14 @@ public class AccountsModule extends DashboardModule {
|
|||
accountsVBox.getChildren().clear();
|
||||
accountsVBox.getChildren().addAll(nodes);
|
||||
}));
|
||||
totalAssetsLabel.setText("Computing...");
|
||||
totalAssetsLabel.setDisable(true);
|
||||
Profile.getCurrent().dataSource().getCombinedAccountBalances()
|
||||
.thenApply(CurrencyUtil::formatMoneyValues)
|
||||
.thenAccept(s -> Platform.runLater(() -> {
|
||||
totalAssetsLabel.setText(s);
|
||||
totalAssetsLabel.setDisable(false);
|
||||
}));
|
||||
}
|
||||
|
||||
private static Node buildMiniAccountTile(Account account) {
|
||||
|
|
|
@ -17,6 +17,7 @@ rather than with your own CSS.
|
|||
-fx-theme-account-type-checking: rgb(3, 127, 252);
|
||||
-fx-theme-account-type-savings: rgb(57, 158, 74);
|
||||
-fx-theme-account-type-credit-card: rgb(207, 8, 68);
|
||||
-fx-theme-account-type-brokerage: rgb(130, 17, 242);
|
||||
}
|
||||
|
||||
.root {
|
||||
|
|
Loading…
Reference in New Issue