com.jscriptive.moneyfx.ui.account.AccountFrame.java Source code

Java tutorial

Introduction

Here is the source code for com.jscriptive.moneyfx.ui.account.AccountFrame.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.jscriptive.moneyfx.ui.account;

import com.jscriptive.moneyfx.configuration.Configuration;
import com.jscriptive.moneyfx.importer.TransactionExtractor;
import com.jscriptive.moneyfx.importer.TransactionExtractorProvider;
import com.jscriptive.moneyfx.model.*;
import com.jscriptive.moneyfx.repository.*;
import com.jscriptive.moneyfx.ui.account.dialog.AccountDialog;
import com.jscriptive.moneyfx.ui.event.ShowTransactionsEvent;
import com.jscriptive.moneyfx.ui.item.AccountItem;
import com.jscriptive.moneyfx.ui.transaction.dialog.TransactionImportDialog;
import com.jscriptive.moneyfx.util.CurrencyFormat;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.util.Pair;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import java.io.File;
import java.math.BigDecimal;
import java.net.URI;
import java.net.URL;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.stream.DoubleStream;

import static com.jscriptive.moneyfx.ui.event.TabSelectionEvent.TAB_SELECTION;
import static java.lang.Math.abs;
import static javafx.scene.control.Alert.AlertType.CONFIRMATION;
import static javafx.scene.control.ButtonType.OK;
import static javafx.scene.input.KeyCode.DELETE;

/**
 * @author jscriptive.com
 */
public class AccountFrame implements Initializable {

    private static Logger log = Logger.getLogger(AccountFrame.class);

    @FXML
    private TableView<AccountItem> dataTable;
    @FXML
    private TableColumn<AccountItem, String> bankColumn;
    @FXML
    private TableColumn<AccountItem, String> countryColumn;
    @FXML
    private TableColumn<AccountItem, String> numberColumn;
    @FXML
    private TableColumn<AccountItem, String> nameColumn;
    @FXML
    private TableColumn<AccountItem, String> typeColumn;
    @FXML
    private TableColumn<AccountItem, BigDecimal> balanceColumn;
    @FXML
    private TableColumn<AccountItem, LocalDate> balanceDateColumn;
    @FXML
    private Label dataSummaryLabel;

    private final ObservableList<AccountItem> accountData = FXCollections.observableArrayList();

    private CountryRepository countryRepository;
    private BankRepository bankRepository;
    private AccountRepository accountRepository;
    private CategoryRepository categoryRepository;
    private TransactionRepository transactionRepository;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        initializeRepositories();
        setupAccountTable();
        initializeColumns();
    }

    private void initializeRepositories() {
        countryRepository = RepositoryProvider.getInstance().getCountryRepository();
        bankRepository = RepositoryProvider.getInstance().getBankRepository();
        accountRepository = RepositoryProvider.getInstance().getAccountRepository();
        categoryRepository = RepositoryProvider.getInstance().getCategoryRepository();
        transactionRepository = RepositoryProvider.getInstance().getTransactionRepository();
    }

    private void setupAccountTable() {
        dataTable.setItems(accountData);
        dataTable.addEventHandler(TAB_SELECTION, event -> loadAccountData());
    }

    private void loadAccountData() {
        accountData.clear();
        accountRepository.findAll().forEach(account -> accountData.add(new AccountItem(account)));
        Platform.runLater(() -> dataSummaryLabel
                .setText("Accounts: " + accountData.size() + ", balance: " + getAbsSum(accountData)));
    }

    private void initializeColumns() {
        bankColumn.setCellValueFactory(cellData -> cellData.getValue().bankProperty());
        countryColumn.setCellValueFactory(cellData -> cellData.getValue().countryProperty());
        numberColumn.setCellValueFactory(cellData -> cellData.getValue().numberProperty());
        nameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
        typeColumn.setCellValueFactory(cellData -> cellData.getValue().typeProperty());
        balanceColumn.setCellValueFactory(cellData -> cellData.getValue().balanceProperty());
        balanceDateColumn.setCellValueFactory(cellData -> cellData.getValue().balanceDateProperty());
    }

    public void addAccountFired(ActionEvent actionEvent) {
        AccountDialog dialog = new AccountDialog();
        Optional<AccountItem> accountItem = dialog.showAndWait();
        if (accountItem.isPresent()) {
            accountData.add(accountItem.get());
            persistAccount(accountItem.get());
            Platform.runLater(() -> dataSummaryLabel
                    .setText("Accounts: " + accountData.size() + ", balance: " + getAbsSum(accountData)));
        }
    }

    public void importTransactionsFired(ActionEvent actionEvent) {
        TransactionImportDialog dialog = new TransactionImportDialog(bankRepository.findAll());
        Optional<Pair<String, File>> result = dialog.showAndWait();
        if (result.isPresent()) {
            String bank = result.get().getKey();
            File file = result.get().getValue();
            TransactionExtractor extractor = TransactionExtractorProvider.getInstance()
                    .getTransactionExtractor(bank);
            Account account = extractAccountData(file.toURI(), extractor);
            if (account != null) {
                extractTransactionData(file.toURI(), extractor, account);
                Platform.runLater(() -> dataSummaryLabel
                        .setText("Accounts: " + accountData.size() + ", balance: " + getAbsSum(accountData)));
            }
            TabPane tabPane = (TabPane) dataTable.getScene().lookup("#tabPane");
            tabPane.getSelectionModel().select(1);
        }
    }

    private Account extractAccountData(URI file, TransactionExtractor extractor) {
        Account extracted = extractor.extractAccountData(file);
        Account found = accountRepository.findByNumber(extracted.getNumber());
        if (found == null) {
            AccountDialog dialog = new AccountDialog(new AccountItem(extracted));
            Optional<AccountItem> accountItem = dialog.showAndWait();
            if (accountItem.isPresent()) {
                found = persistAccount(accountItem.get());
            }
        }
        return found;
    }

    private Account persistAccount(AccountItem item) {
        Bank bank = bankRepository.findByName(item.getBank());
        if (bank == null) {
            Country country = countryRepository.findByCode(item.getCountry());
            if (country == null) {
                country = Country.fromCountryCode(item.getCountry());
                countryRepository.save(country);
            }
            bank = new Bank(item.getBank(), country);
            bank.setTransferConceptRegex(Configuration.getInstance().getTransferConceptRegexFor(bank));
            bankRepository.save(bank);
        }
        Account account = new Account(bank, item.getNumber(), item.getName(), item.getType(), item.getBalance(),
                item.getBalanceDate());
        accountRepository.save(account);
        return account;
    }

    private void extractTransactionData(URI file, TransactionExtractor extractor, Account account) {
        List<Transaction> transactions = extractor.extractTransactionData(file);
        account.updateBalance(transactions);
        Category other = getDefaultCategory();
        List<Transaction> all = transactionRepository.findAll();
        transactions.forEach(trx -> {
            trx.setAccount(account);
            trx.setCategory(other);
            if (all.contains(trx)) {
                log.debug("Found transaction that has a duplicate in DB: " + trx);
            } else {
                persistTransaction(trx);
            }
        });
    }

    private Category getDefaultCategory() {
        Category other = categoryRepository.findByName(Category.OTHER.getName());
        if (other == null) {
            other = Category.OTHER;
            categoryRepository.save(other);
        }
        return other;
    }

    private void persistTransaction(Transaction trx) {
        Category category = categoryRepository.findByName(trx.getCategory().getName());
        if (category == null) {
            category = new Category(trx.getCategory().getName());
            categoryRepository.save(category);
            trx.setCategory(category);
        }
        transactionRepository.save(trx);
    }

    public void contextMenuItemEditSelected(ActionEvent actionEvent) {
        editAccount();
    }

    public void mouseClicked(MouseEvent event) {
        if (event.getClickCount() == 2) {
            editAccount();
        }
    }

    private void editAccount() {
        AccountItem selectedItem = dataTable.getSelectionModel().getSelectedItem();
        if (selectedItem != null) {
            AccountDialog dialog = new AccountDialog(selectedItem);
            Optional<AccountItem> result = dialog.showAndWait();
            if (result.isPresent()) {
                Account account = accountRepository.findByNumber(result.get().getNumber());
                String countryCode = result.get().getCountry();
                if (!StringUtils.equals(account.getBank().getCountryCode(), countryCode)) {
                    Country country = countryRepository.findByCode(countryCode);
                    if (country == null) {
                        country = Country.fromCountryCode(countryCode);
                        countryRepository.save(country);
                    }
                    account.getBank().setCountry(country);
                    account.getBank().setTransferConceptRegex(
                            Configuration.getInstance().getTransferConceptRegexFor(account.getBank()));
                    bankRepository.save(account.getBank());
                }
                account.setName(result.get().getName());
                account.setType(result.get().getType());
                account.setBalance(result.get().getBalance());
                account.setBalanceDate(result.get().getBalanceDate());
                accountRepository.save(account);
                accountData.set(dataTable.getSelectionModel().getSelectedIndex(), result.get());
                Platform.runLater(() -> dataSummaryLabel
                        .setText("Accounts: " + accountData.size() + ", balance: " + getAbsSum(accountData)));
            }
        }
    }

    public void contextMenuItemDeleteSelected(ActionEvent actionEvent) {
        deleteAccount();
    }

    public void keyTyped(KeyEvent event) {
        KeyCode keyCode = event.getCode();
        if (DELETE == keyCode) {
            deleteAccount();
        }
    }

    private void deleteAccount() {
        Alert alert = new Alert(CONFIRMATION);
        alert.setTitle("Delete account");
        alert.setHeaderText("Confirm delete account");
        AccountItem selectedItem = dataTable.getSelectionModel().getSelectedItem();
        alert.setContentText(String.format(
                "Are you sure you want to delete the selected account: %s %s? All transactions of that account will also be deleted.",
                selectedItem.getBank(), selectedItem.getNumber()));
        Optional<ButtonType> result = alert.showAndWait();
        if (result.get() == OK) {
            int selectedIndex = dataTable.getSelectionModel().getSelectedIndex();
            Account account = accountRepository.findByNumber(selectedItem.getNumber());
            transactionRepository.removeByAccount(account);
            accountRepository.remove(account);
            accountData.remove(selectedIndex);
        }
    }

    public void contextMenuShowTransactions(ActionEvent actionEvent) {
        AccountItem selectedItem = dataTable.getSelectionModel().getSelectedItem();
        Account persistedAccount = accountRepository.findByNumber(selectedItem.getNumber());
        TransactionFilter filter = new TransactionFilter();
        filter.setAccount(persistedAccount);
        dataTable.getScene().lookup("#tabPane").fireEvent(new ShowTransactionsEvent(filter));
    }

    private String getAbsSum(List<AccountItem> accountItems) {
        double sum = accountItems.parallelStream()
                .flatMapToDouble(item -> DoubleStream.of(abs(item.getBalance().doubleValue()))).sum();
        return CurrencyFormat.getInstance().format(sum);
    }
}