Refactored repository access to use fancy generic method.
This commit is contained in:
parent
4600470cdb
commit
f0b061c34d
|
@ -1,6 +1,8 @@
|
|||
package com.andrewlalis.perfin.control;
|
||||
|
||||
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
||||
import com.andrewlalis.perfin.data.AccountHistoryItemRepository;
|
||||
import com.andrewlalis.perfin.data.AccountRepository;
|
||||
import com.andrewlalis.perfin.data.util.DateUtil;
|
||||
import com.andrewlalis.perfin.model.Account;
|
||||
import com.andrewlalis.perfin.model.Profile;
|
||||
|
@ -62,7 +64,8 @@ public class AccountViewController implements RouteSelectionListener {
|
|||
accountNumberLabel.setText(account.getAccountNumber());
|
||||
accountCurrencyLabel.setText(account.getCurrency().getDisplayName());
|
||||
accountCreatedAtLabel.setText(DateUtil.formatUTCAsLocalWithZone(account.getCreatedAt()));
|
||||
Profile.getCurrent().getDataSource().getAccountBalanceText(account, accountBalanceLabel::setText);
|
||||
Profile.getCurrent().getDataSource().getAccountBalanceText(account)
|
||||
.thenAccept(accountBalanceLabel::setText);
|
||||
|
||||
reloadHistory();
|
||||
}
|
||||
|
@ -93,7 +96,7 @@ public class AccountViewController implements RouteSelectionListener {
|
|||
"later if you need to."
|
||||
);
|
||||
if (confirmResult) {
|
||||
Profile.getCurrent().getDataSource().useAccountRepository(repo -> repo.archive(account.id));
|
||||
Profile.getCurrent().getDataSource().useRepo(AccountRepository.class, repo -> repo.archive(account.id));
|
||||
router.replace("accounts");
|
||||
}
|
||||
}
|
||||
|
@ -104,7 +107,7 @@ public class AccountViewController implements RouteSelectionListener {
|
|||
"status?"
|
||||
);
|
||||
if (confirm) {
|
||||
Profile.getCurrent().getDataSource().useAccountRepository(repo -> repo.unarchive(account.id));
|
||||
Profile.getCurrent().getDataSource().useRepo(AccountRepository.class, repo -> repo.unarchive(account.id));
|
||||
router.replace("accounts");
|
||||
}
|
||||
}
|
||||
|
@ -119,31 +122,27 @@ public class AccountViewController implements RouteSelectionListener {
|
|||
"want to hide it."
|
||||
);
|
||||
if (confirm) {
|
||||
Profile.getCurrent().getDataSource().useAccountRepository(repo -> repo.delete(account));
|
||||
Profile.getCurrent().getDataSource().useRepo(AccountRepository.class, repo -> repo.delete(account));
|
||||
router.replace("accounts");
|
||||
}
|
||||
}
|
||||
|
||||
@FXML public void loadMoreHistory() {
|
||||
Thread.ofVirtual().start(() -> {
|
||||
try (var historyRepo = Profile.getCurrent().getDataSource().getAccountHistoryItemRepository()) {
|
||||
List<AccountHistoryItem> historyItems = historyRepo.findMostRecentForAccount(
|
||||
account.id,
|
||||
loadHistoryFrom,
|
||||
historyLoadSize
|
||||
);
|
||||
if (historyItems.size() < historyLoadSize) {
|
||||
Platform.runLater(() -> loadMoreHistoryButton.setDisable(true));
|
||||
} else {
|
||||
loadHistoryFrom = historyItems.getLast().getTimestamp();
|
||||
}
|
||||
List<? extends Node> nodes = historyItems.stream()
|
||||
.map(item -> AccountHistoryItemTile.forItem(item, historyRepo, this))
|
||||
.toList();
|
||||
Platform.runLater(() -> historyItemsVBox.getChildren().addAll(nodes));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
Profile.getCurrent().getDataSource().useRepoAsync(AccountHistoryItemRepository.class, repo -> {
|
||||
List<AccountHistoryItem> historyItems = repo.findMostRecentForAccount(
|
||||
account.id,
|
||||
loadHistoryFrom,
|
||||
historyLoadSize
|
||||
);
|
||||
if (historyItems.size() < historyLoadSize) {
|
||||
Platform.runLater(() -> loadMoreHistoryButton.setDisable(true));
|
||||
} else {
|
||||
loadHistoryFrom = historyItems.getLast().getTimestamp();
|
||||
}
|
||||
List<? extends Node> nodes = historyItems.stream()
|
||||
.map(item -> AccountHistoryItemTile.forItem(item, repo, this))
|
||||
.toList();
|
||||
Platform.runLater(() -> historyItemsVBox.getChildren().addAll(nodes));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.andrewlalis.perfin.control;
|
||||
|
||||
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;
|
||||
|
@ -48,14 +49,14 @@ public class AccountsViewController implements RouteSelectionListener {
|
|||
|
||||
public void refreshAccounts() {
|
||||
Profile.whenLoaded(profile -> {
|
||||
Thread.ofVirtual().start(() -> profile.getDataSource().useAccountRepository(repo -> {
|
||||
profile.getDataSource().useRepoAsync(AccountRepository.class, repo -> {
|
||||
List<Account> accounts = repo.findAllOrderedByRecentHistory();
|
||||
Platform.runLater(() -> accountsPane.getChildren()
|
||||
.setAll(accounts.stream()
|
||||
.map(AccountTile::new)
|
||||
.toList()
|
||||
));
|
||||
}));
|
||||
});
|
||||
// Compute grand totals!
|
||||
Thread.ofVirtual().start(() -> {
|
||||
var totals = profile.getDataSource().getCombinedAccountBalances();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.andrewlalis.perfin.control;
|
||||
|
||||
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
||||
import com.andrewlalis.perfin.data.BalanceRecordRepository;
|
||||
import com.andrewlalis.perfin.data.util.CurrencyUtil;
|
||||
import com.andrewlalis.perfin.data.util.DateUtil;
|
||||
import com.andrewlalis.perfin.model.Attachment;
|
||||
|
@ -40,19 +41,16 @@ public class BalanceRecordViewController implements RouteSelectionListener {
|
|||
timestampLabel.setText(DateUtil.formatUTCAsLocalWithZone(balanceRecord.getTimestamp()));
|
||||
balanceLabel.setText(CurrencyUtil.formatMoney(balanceRecord.getMoneyAmount()));
|
||||
currencyLabel.setText(balanceRecord.getCurrency().getDisplayName());
|
||||
|
||||
Thread.ofVirtual().start(() -> Profile.getCurrent().getDataSource().useBalanceRecordRepository(repo -> {
|
||||
Profile.getCurrent().getDataSource().useRepoAsync(BalanceRecordRepository.class, repo -> {
|
||||
List<Attachment> attachments = repo.findAttachments(balanceRecord.id);
|
||||
Platform.runLater(() -> attachmentsViewPane.setAttachments(attachments));
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
@FXML public void delete() {
|
||||
boolean confirm = Popups.confirm("Are you sure you want to delete this balance record? This may have an effect on the derived balance of your account, as shown in Perfin.");
|
||||
if (confirm) {
|
||||
Profile.getCurrent().getDataSource().useBalanceRecordRepository(repo -> {
|
||||
repo.deleteById(balanceRecord.id);
|
||||
});
|
||||
Profile.getCurrent().getDataSource().useRepo(BalanceRecordRepository.class, repo -> repo.deleteById(balanceRecord.id));
|
||||
router.navigateBackAndClear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.andrewlalis.perfin.control;
|
||||
|
||||
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
||||
import com.andrewlalis.perfin.data.AccountRepository;
|
||||
import com.andrewlalis.perfin.data.BalanceRecordRepository;
|
||||
import com.andrewlalis.perfin.data.util.CurrencyUtil;
|
||||
import com.andrewlalis.perfin.data.util.DateUtil;
|
||||
import com.andrewlalis.perfin.data.util.FileUtil;
|
||||
|
@ -59,12 +61,12 @@ public class CreateBalanceRecordController implements RouteSelectionListener {
|
|||
return;
|
||||
}
|
||||
BigDecimal reportedBalance = new BigDecimal(newValue);
|
||||
Thread.ofVirtual().start(() -> Profile.getCurrent().getDataSource().useAccountRepository(repo -> {
|
||||
Profile.getCurrent().getDataSource().useRepoAsync(AccountRepository.class, repo -> {
|
||||
BigDecimal derivedBalance = repo.deriveCurrentBalance(account.id);
|
||||
Platform.runLater(() -> balanceWarningLabel.visibleProperty().set(
|
||||
!reportedBalance.setScale(derivedBalance.scale(), RoundingMode.HALF_UP).equals(derivedBalance)
|
||||
));
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
var formValid = timestampValid.and(balanceValid);
|
||||
|
@ -83,12 +85,12 @@ public class CreateBalanceRecordController implements RouteSelectionListener {
|
|||
public void onRouteSelected(Object context) {
|
||||
this.account = (Account) context;
|
||||
timestampField.setText(LocalDateTime.now().format(DateUtil.DEFAULT_DATETIME_FORMAT));
|
||||
Thread.ofVirtual().start(() -> Profile.getCurrent().getDataSource().useAccountRepository(repo -> {
|
||||
Profile.getCurrent().getDataSource().useRepoAsync(AccountRepository.class, repo -> {
|
||||
BigDecimal value = repo.deriveCurrentBalance(account.id);
|
||||
Platform.runLater(() -> balanceField.setText(
|
||||
CurrencyUtil.formatMoneyAsBasicNumber(new MoneyValue(value, account.getCurrency()))
|
||||
));
|
||||
}));
|
||||
});
|
||||
attachmentSelectionArea.clear();
|
||||
}
|
||||
|
||||
|
@ -102,7 +104,7 @@ public class CreateBalanceRecordController implements RouteSelectionListener {
|
|||
localTimestamp.atZone(ZoneId.systemDefault()).format(DateUtil.DEFAULT_DATETIME_FORMAT_WITH_ZONE)
|
||||
));
|
||||
if (confirm && confirmIfInconsistentBalance(reportedBalance)) {
|
||||
Profile.getCurrent().getDataSource().useBalanceRecordRepository(repo -> {
|
||||
Profile.getCurrent().getDataSource().useRepo(BalanceRecordRepository.class, repo -> {
|
||||
repo.insert(
|
||||
DateUtil.localToUTC(localTimestamp),
|
||||
account.id,
|
||||
|
@ -120,12 +122,10 @@ public class CreateBalanceRecordController implements RouteSelectionListener {
|
|||
}
|
||||
|
||||
private boolean confirmIfInconsistentBalance(BigDecimal reportedBalance) {
|
||||
BigDecimal currentDerivedBalance;
|
||||
try (var accountRepo = Profile.getCurrent().getDataSource().getAccountRepository()) {
|
||||
currentDerivedBalance = accountRepo.deriveCurrentBalance(account.id);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
BigDecimal currentDerivedBalance = Profile.getCurrent().getDataSource().mapRepo(
|
||||
AccountRepository.class,
|
||||
repo -> repo.deriveCurrentBalance(account.id)
|
||||
);
|
||||
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(
|
||||
CurrencyUtil.formatMoney(new MoneyValue(reportedBalance, account.getCurrency())),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.andrewlalis.perfin.control;
|
||||
|
||||
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
||||
import com.andrewlalis.perfin.data.TransactionRepository;
|
||||
import com.andrewlalis.perfin.data.pagination.PageRequest;
|
||||
import com.andrewlalis.perfin.data.pagination.Sort;
|
||||
import com.andrewlalis.perfin.data.util.CurrencyUtil;
|
||||
|
@ -104,23 +105,25 @@ public class EditTransactionController implements RouteSelectionListener {
|
|||
}
|
||||
|
||||
@FXML public void save() {
|
||||
LocalDateTime utcTimestamp = DateUtil.localToUTC(parseTimestamp());
|
||||
BigDecimal amount = new BigDecimal(amountField.getText());
|
||||
Currency currency = currencyChoiceBox.getValue();
|
||||
String description = getSanitizedDescription();
|
||||
CreditAndDebitAccounts linkedAccounts = getSelectedAccounts();
|
||||
List<Path> attachments = attachmentsSelectionArea.getSelectedFiles();
|
||||
Profile.getCurrent().getDataSource().useTransactionRepository(repo -> {
|
||||
repo.insert(
|
||||
utcTimestamp,
|
||||
amount,
|
||||
currency,
|
||||
description,
|
||||
linkedAccounts,
|
||||
attachments
|
||||
);
|
||||
});
|
||||
router.navigateBackAndClear();
|
||||
final long idToNavigate;
|
||||
if (transaction == null) {
|
||||
LocalDateTime utcTimestamp = DateUtil.localToUTC(parseTimestamp());
|
||||
BigDecimal amount = new BigDecimal(amountField.getText());
|
||||
Currency currency = currencyChoiceBox.getValue();
|
||||
String description = getSanitizedDescription();
|
||||
CreditAndDebitAccounts linkedAccounts = getSelectedAccounts();
|
||||
List<Path> attachments = attachmentsSelectionArea.getSelectedFiles();
|
||||
Profile.getCurrent().getDataSource().useRepo(TransactionRepository.class, repo -> {
|
||||
repo.insert(
|
||||
utcTimestamp,
|
||||
amount,
|
||||
currency,
|
||||
description,
|
||||
linkedAccounts,
|
||||
attachments
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@FXML public void cancel() {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.andrewlalis.perfin.control;
|
||||
|
||||
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.Attachment;
|
||||
|
@ -44,27 +45,19 @@ public class TransactionViewController {
|
|||
amountLabel.setText(CurrencyUtil.formatMoney(transaction.getMoneyAmount()));
|
||||
timestampLabel.setText(DateUtil.formatUTCAsLocalWithZone(transaction.getTimestamp()));
|
||||
descriptionLabel.setText(transaction.getDescription());
|
||||
|
||||
Thread.ofVirtual().start(() -> {
|
||||
Profile.getCurrent().getDataSource().useTransactionRepository(repo -> {
|
||||
CreditAndDebitAccounts accounts = repo.findLinkedAccounts(transaction.id);
|
||||
Platform.runLater(() -> {
|
||||
accounts.ifDebit(acc -> {
|
||||
debitAccountLink.setText(acc.getShortName());
|
||||
debitAccountLink.setOnAction(event -> router.navigate("account", acc));
|
||||
});
|
||||
accounts.ifCredit(acc -> {
|
||||
creditAccountLink.setText(acc.getShortName());
|
||||
creditAccountLink.setOnAction(event -> router.navigate("account", acc));
|
||||
});
|
||||
Profile.getCurrent().getDataSource().useRepoAsync(TransactionRepository.class, repo -> {
|
||||
CreditAndDebitAccounts accounts = repo.findLinkedAccounts(transaction.id);
|
||||
List<Attachment> attachments = repo.findAttachments(transaction.id);
|
||||
Platform.runLater(() -> {
|
||||
accounts.ifDebit(acc -> {
|
||||
debitAccountLink.setText(acc.getShortName());
|
||||
debitAccountLink.setOnAction(event -> router.navigate("account", acc));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Thread.ofVirtual().start(() -> {
|
||||
Profile.getCurrent().getDataSource().useTransactionRepository(repo -> {
|
||||
List<Attachment> attachments = repo.findAttachments(transaction.id);
|
||||
Platform.runLater(() -> attachmentsViewPane.setAttachments(attachments));
|
||||
accounts.ifCredit(acc -> {
|
||||
creditAccountLink.setText(acc.getShortName());
|
||||
creditAccountLink.setOnAction(event -> router.navigate("account", acc));
|
||||
});
|
||||
attachmentsViewPane.setAttachments(attachments);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -84,10 +77,8 @@ public class TransactionViewController {
|
|||
"it's derived from the most recent balance-record, and transactions."
|
||||
);
|
||||
if (confirm) {
|
||||
Profile.getCurrent().getDataSource().useTransactionRepository(repo -> {
|
||||
repo.delete(transaction.id);
|
||||
router.replace("transactions");
|
||||
});
|
||||
Profile.getCurrent().getDataSource().useRepo(TransactionRepository.class, repo -> repo.delete(transaction.id));
|
||||
router.replace("transactions");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.andrewlalis.perfin.control;
|
||||
|
||||
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
||||
import com.andrewlalis.perfin.data.AccountRepository;
|
||||
import com.andrewlalis.perfin.data.TransactionRepository;
|
||||
import com.andrewlalis.perfin.data.pagination.Page;
|
||||
import com.andrewlalis.perfin.data.pagination.PageRequest;
|
||||
import com.andrewlalis.perfin.data.pagination.Sort;
|
||||
|
@ -122,27 +124,25 @@ public class TransactionsViewController implements RouteSelectionListener {
|
|||
transactionsVBox.getChildren().clear(); // Clear the transactions before reload initially.
|
||||
|
||||
// Refresh account filter options.
|
||||
Thread.ofVirtual().start(() -> {
|
||||
Profile.getCurrent().getDataSource().useAccountRepository(repo -> {
|
||||
List<Account> accounts = repo.findAll(PageRequest.unpaged(Sort.asc("name"))).items();
|
||||
accounts.add(null);
|
||||
Platform.runLater(() -> {
|
||||
filterByAccountComboBox.getItems().clear();
|
||||
filterByAccountComboBox.getItems().addAll(accounts);
|
||||
filterByAccountComboBox.getSelectionModel().selectLast();
|
||||
filterByAccountComboBox.getButtonCell().updateIndex(accounts.size() - 1);
|
||||
});
|
||||
Profile.getCurrent().getDataSource().useRepoAsync(AccountRepository.class, repo -> {
|
||||
List<Account> accounts = repo.findAll(PageRequest.unpaged(Sort.asc("name"))).items();
|
||||
accounts.add(null);
|
||||
Platform.runLater(() -> {
|
||||
filterByAccountComboBox.getItems().clear();
|
||||
filterByAccountComboBox.getItems().addAll(accounts);
|
||||
filterByAccountComboBox.getSelectionModel().selectLast();
|
||||
filterByAccountComboBox.getButtonCell().updateIndex(accounts.size() - 1);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// If a transaction id is given in the route context, navigate to the page it's on and select it.
|
||||
if (context instanceof RouteContext ctx && ctx.selectedTransactionId != null) {
|
||||
Thread.ofVirtual().start(() -> {
|
||||
Profile.getCurrent().getDataSource().useTransactionRepository(repo -> {
|
||||
repo.findById(ctx.selectedTransactionId).ifPresent(tx -> {
|
||||
long offset = repo.countAllAfter(tx.id);
|
||||
int pageNumber = (int) (offset / paginationControls.getItemsPerPage()) + 1;
|
||||
Profile.getCurrent().getDataSource().useRepoAsync(TransactionRepository.class, repo -> {
|
||||
repo.findById(ctx.selectedTransactionId).ifPresent(tx -> {
|
||||
long offset = repo.countAllAfter(tx.id);
|
||||
int pageNumber = (int) (offset / paginationControls.getItemsPerPage()) + 1;
|
||||
Platform.runLater(() -> {
|
||||
paginationControls.setPage(pageNumber).thenRun(() -> selectedTransaction.set(tx));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@ import java.time.LocalDateTime;
|
|||
import java.util.Currency;
|
||||
import java.util.List;
|
||||
|
||||
public interface AccountEntryRepository extends AutoCloseable {
|
||||
public interface AccountEntryRepository extends Repository, AutoCloseable {
|
||||
long insert(
|
||||
LocalDateTime timestamp,
|
||||
long accountId,
|
||||
|
|
|
@ -9,7 +9,7 @@ import java.time.LocalDateTime;
|
|||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface AccountHistoryItemRepository extends AutoCloseable {
|
||||
public interface AccountHistoryItemRepository extends Repository, AutoCloseable {
|
||||
void recordAccountEntry(LocalDateTime timestamp, long accountId, long entryId);
|
||||
void recordBalanceRecord(LocalDateTime timestamp, long accountId, long recordId);
|
||||
void recordText(LocalDateTime timestamp, long accountId, String text);
|
||||
|
|
|
@ -13,7 +13,7 @@ import java.util.List;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public interface AccountRepository extends AutoCloseable {
|
||||
public interface AccountRepository extends Repository, AutoCloseable {
|
||||
long insert(AccountType type, String accountNumber, String name, Currency currency);
|
||||
Page<Account> findAll(PageRequest pagination);
|
||||
List<Account> findAllOrderedByRecentHistory();
|
||||
|
|
|
@ -5,7 +5,7 @@ import com.andrewlalis.perfin.model.Attachment;
|
|||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface AttachmentRepository extends AutoCloseable {
|
||||
public interface AttachmentRepository extends Repository, AutoCloseable {
|
||||
Attachment insert(Path sourcePath);
|
||||
Optional<Attachment> findById(long attachmentId);
|
||||
Optional<Attachment> findByIdentifier(String identifier);
|
||||
|
|
|
@ -10,7 +10,7 @@ import java.util.Currency;
|
|||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface BalanceRecordRepository extends AutoCloseable {
|
||||
public interface BalanceRecordRepository extends Repository, AutoCloseable {
|
||||
long insert(LocalDateTime utcTimestamp, long accountId, BigDecimal balance, Currency currency, List<Path> attachments);
|
||||
BalanceRecord findLatestByAccountId(long accountId);
|
||||
Optional<BalanceRecord> findClosestBefore(long accountId, LocalDateTime utcTimestamp);
|
||||
|
|
|
@ -2,8 +2,6 @@ package com.andrewlalis.perfin.data;
|
|||
|
||||
import com.andrewlalis.perfin.data.pagination.PageRequest;
|
||||
import com.andrewlalis.perfin.data.util.CurrencyUtil;
|
||||
import com.andrewlalis.perfin.data.util.DbUtil;
|
||||
import com.andrewlalis.perfin.data.util.ThrowableConsumer;
|
||||
import com.andrewlalis.perfin.model.Account;
|
||||
import com.andrewlalis.perfin.model.AccountType;
|
||||
import com.andrewlalis.perfin.model.MoneyValue;
|
||||
|
@ -11,11 +9,11 @@ import javafx.application.Platform;
|
|||
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Currency;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Interface for methods to obtain any data from a {@link com.andrewlalis.perfin.model.Profile}
|
||||
|
@ -35,30 +33,70 @@ public interface DataSource {
|
|||
AttachmentRepository getAttachmentRepository();
|
||||
AccountHistoryItemRepository getAccountHistoryItemRepository();
|
||||
|
||||
default void useAccountRepository(ThrowableConsumer<AccountRepository> repoConsumer) {
|
||||
DbUtil.useClosable(this::getAccountRepository, repoConsumer);
|
||||
// Repository helper methods:
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default <R extends Repository, T> T mapRepo(Class<R> repoType, Function<R, T> action) {
|
||||
Supplier<R> repoSupplier = getRepo(repoType);
|
||||
if (repoSupplier == null) throw new IllegalArgumentException("Repository type " + repoType + " is not supported.");
|
||||
boolean repoCloseable = Arrays.asList(repoType.getInterfaces()).contains(AutoCloseable.class);
|
||||
if (repoCloseable) {
|
||||
try (AutoCloseable c = (AutoCloseable) repoSupplier.get()) {
|
||||
R repo = (R) c;
|
||||
return action.apply(repo);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
R repo = repoSupplier.get();
|
||||
return action.apply(repo);
|
||||
}
|
||||
}
|
||||
|
||||
default void useBalanceRecordRepository(ThrowableConsumer<BalanceRecordRepository> repoConsumer) {
|
||||
DbUtil.useClosable(this::getBalanceRecordRepository, repoConsumer);
|
||||
default <R extends Repository, T> CompletableFuture<T> mapRepoAsync(Class<R> repoType, Function<R, T> action) {
|
||||
CompletableFuture<T> cf = new CompletableFuture<>();
|
||||
Thread.ofVirtual().start(() -> {
|
||||
cf.complete(mapRepo(repoType, action));
|
||||
});
|
||||
return cf;
|
||||
}
|
||||
|
||||
default void useTransactionRepository(ThrowableConsumer<TransactionRepository> repoConsumer) {
|
||||
DbUtil.useClosable(this::getTransactionRepository, repoConsumer);
|
||||
default <R extends Repository> void useRepo(Class<R> repoType, Consumer<R> action) {
|
||||
mapRepo(repoType, (Function<R, Void>) repo -> {
|
||||
action.accept(repo);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
default void useAttachmentRepository(ThrowableConsumer<AttachmentRepository> repoConsumer) {
|
||||
DbUtil.useClosable(this::getAttachmentRepository, repoConsumer);
|
||||
default <R extends Repository> CompletableFuture<Void> useRepoAsync(Class<R> repoType, Consumer<R> action) {
|
||||
return mapRepoAsync(repoType, repo -> {
|
||||
action.accept(repo);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <R extends Repository> Supplier<R> getRepo(Class<R> type) {
|
||||
final Map<Class<? extends Repository>, Supplier<? extends Repository>> repoSuppliers = Map.of(
|
||||
AccountRepository.class, this::getAccountRepository,
|
||||
BalanceRecordRepository.class, this::getBalanceRecordRepository,
|
||||
TransactionRepository.class, this::getTransactionRepository,
|
||||
AttachmentRepository.class, this::getAttachmentRepository,
|
||||
AccountHistoryItemRepository.class, this::getAccountHistoryItemRepository
|
||||
);
|
||||
return (Supplier<R>) repoSuppliers.get(type);
|
||||
}
|
||||
|
||||
// Utility methods:
|
||||
|
||||
default void getAccountBalanceText(Account account, Consumer<String> balanceConsumer) {
|
||||
Thread.ofVirtual().start(() -> useAccountRepository(repo -> {
|
||||
default CompletableFuture<String> getAccountBalanceText(Account account) {
|
||||
CompletableFuture<String> cf = new CompletableFuture<>();
|
||||
mapRepoAsync(AccountRepository.class, repo -> {
|
||||
BigDecimal balance = repo.deriveCurrentBalance(account.id);
|
||||
MoneyValue money = new MoneyValue(balance, account.getCurrency());
|
||||
Platform.runLater(() -> balanceConsumer.accept(CurrencyUtil.formatMoney(money)));
|
||||
}));
|
||||
return CurrencyUtil.formatMoney(money);
|
||||
}).thenAccept(s -> Platform.runLater(() -> cf.complete(s)));
|
||||
return cf;
|
||||
}
|
||||
|
||||
default Map<Currency, BigDecimal> getCombinedAccountBalances() {
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
package com.andrewlalis.perfin.data;
|
||||
|
||||
public interface Repository {
|
||||
}
|
|
@ -14,7 +14,7 @@ import java.util.List;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public interface TransactionRepository extends AutoCloseable {
|
||||
public interface TransactionRepository extends Repository, AutoCloseable {
|
||||
long insert(
|
||||
LocalDateTime utcTimestamp,
|
||||
BigDecimal amount,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.andrewlalis.perfin.view.component;
|
||||
|
||||
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;
|
||||
|
@ -109,13 +110,13 @@ public class AccountSelectionBox extends ComboBox<Account> {
|
|||
|
||||
nameLabel.setText(item.getName() + " (" + item.getAccountNumberSuffix() + ")");
|
||||
if (showBalanceProp.get()) {
|
||||
Thread.ofVirtual().start(() -> Profile.getCurrent().getDataSource().useAccountRepository(repo -> {
|
||||
Profile.getCurrent().getDataSource().useRepoAsync(AccountRepository.class, repo -> {
|
||||
BigDecimal balance = repo.deriveCurrentBalance(item.id);
|
||||
Platform.runLater(() -> {
|
||||
balanceLabel.setText(CurrencyUtil.formatMoney(new MoneyValue(balance, item.getCurrency())));
|
||||
balanceLabel.setVisible(true);
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.andrewlalis.perfin.view.component;
|
||||
|
||||
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;
|
||||
|
@ -80,7 +81,7 @@ public class AccountTile extends BorderPane {
|
|||
Label balanceLabel = new Label("Computing balance...");
|
||||
balanceLabel.getStyleClass().addAll("mono-font");
|
||||
balanceLabel.setDisable(true);
|
||||
Thread.ofVirtual().start(() -> Profile.getCurrent().getDataSource().useAccountRepository(repo -> {
|
||||
Profile.getCurrent().getDataSource().useRepoAsync(AccountRepository.class, repo -> {
|
||||
BigDecimal balance = repo.deriveCurrentBalance(account.id);
|
||||
String text = CurrencyUtil.formatMoney(new MoneyValue(balance, account.getCurrency()));
|
||||
Platform.runLater(() -> {
|
||||
|
@ -92,10 +93,6 @@ public class AccountTile extends BorderPane {
|
|||
}
|
||||
balanceLabel.setDisable(false);
|
||||
});
|
||||
}));
|
||||
Profile.getCurrent().getDataSource().getAccountBalanceText(account, text -> {
|
||||
balanceLabel.setText(text);
|
||||
balanceLabel.setDisable(false);
|
||||
});
|
||||
|
||||
propertiesPane.getChildren().addAll(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.andrewlalis.perfin.view.component;
|
||||
|
||||
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.CreditAndDebitAccounts;
|
||||
|
@ -99,13 +100,9 @@ public class TransactionTile extends BorderPane {
|
|||
}
|
||||
|
||||
private CompletableFuture<CreditAndDebitAccounts> getCreditAndDebitAccounts(Transaction transaction) {
|
||||
CompletableFuture<CreditAndDebitAccounts> cf = new CompletableFuture<>();
|
||||
Thread.ofVirtual().start(() -> {
|
||||
Profile.getCurrent().getDataSource().useTransactionRepository(repo -> {
|
||||
CreditAndDebitAccounts accounts = repo.findLinkedAccounts(transaction.id);
|
||||
cf.complete(accounts);
|
||||
});
|
||||
});
|
||||
return cf;
|
||||
return Profile.getCurrent().getDataSource().mapRepoAsync(
|
||||
TransactionRepository.class,
|
||||
repo -> repo.findLinkedAccounts(transaction.id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue