Skip to content
Snippets Groups Projects
IncomeExpenseController.java 18.3 KiB
Newer Older
package no.ntnu.idatt1002.demo.controller;

import java.awt.event.ActionEvent;
import java.io.IOException;
import java.util.Optional;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Side;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.chart.PieChart.Data;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ComboBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.text.Text;
import no.ntnu.idatt1002.demo.data.Budget.FileHandlingBudget;
import no.ntnu.idatt1002.demo.data.Budget.GeneralBudget;
import no.ntnu.idatt1002.demo.data.Economics.Expense;
import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory;
import no.ntnu.idatt1002.demo.data.Economics.ExpenseRegister;
import no.ntnu.idatt1002.demo.data.Economics.FileHandling;
import no.ntnu.idatt1002.demo.data.Economics.Income;
import no.ntnu.idatt1002.demo.data.Economics.IncomeCategory;
import no.ntnu.idatt1002.demo.data.Economics.IncomeRegister;

public class IncomeExpenseController implements FinanceController {

  @FXML
  private TableColumn<Expense, Double> expAmountCol;

  @FXML
  private TableColumn<Expense, ExpenseCategory> expCategoryCol;

  @FXML
  private TableColumn<Expense, String> expDateCol;

  @FXML
  private TableColumn<Expense, String> expDescriptionCol;

  @FXML
  private TableColumn<Expense, Boolean> expRecurringCol;

  @FXML
  private TableColumn<Income, Double> inAmountCol;

  @FXML
  private TableColumn<Income, IncomeCategory>  inCategoryCol;

  @FXML
  private TableColumn<Income, String>  inDateCol;

  @FXML
  private TableColumn<Income, String>  inDescriptionCol;

  @FXML
  private TableColumn<Income, Boolean> inRecurringCol;

  @FXML
  private TableView<Expense> expenseTableView;

  @FXML
  private TableView<Income> incomeTableView;

  @FXML
  private Text inSum;

  @FXML
  private Text expSum;

  @FXML
  private MenuItem addExpense;

  @FXML
  private MenuItem addIncome;

  @FXML
  private ProgressBar budgetProgress;

  @FXML
  private DatePicker date;

  @FXML
  private Label daysLeftLbl;

  @FXML
  private ComboBox<?> filter;

  @FXML
  private Button returnBtn;

  @FXML
  private Label title;

  @FXML
  private MenuItem editIncomeMenu;

  @FXML
  private MenuItem deleteIncomeMenu;

  @FXML
  private MenuItem editExpenseMenu;

  @FXML
  private MenuItem deleteExpenseMenu;

  private IncomeRegister incomeRegister;

  private ExpenseRegister expenseRegister;

  private GeneralBudget generalBudget;

  private ObservableList<Income> income;

  private ObservableList<Expense> expenses;

  @FXML
  private PieChart expensePieChart;

  @FXML
  private PieChart incomePieChart;

  FileHandling fileHandling;

  @FXML
  public void initialize() throws IOException {
    setColumns();

    //Initialize registers and tableview
    incomeRegister = loadIncomeDataFromFile("Income");
    income = FXCollections.observableArrayList(incomeRegister.getItems());
    incomeTableView.setItems(income);

    expenseRegister = loadExpenseDataFromFile("Expense");
    expenses = FXCollections.observableArrayList(expenseRegister.getItems());
    expenseTableView.setItems(expenses);

    //Setting pie chart values to correspond with the registers
    //Initialize sum field under the tableview
   // inSum.setText(String.valueOf(incomeRegister.getTotalSum()));
    //expSum.setText(String.valueOf(expenseRegister.getTotalSum()));
  }

  private void setColumns() {
    inDateCol.setCellValueFactory(new PropertyValueFactory<Income, String>("date"));
    inAmountCol.setCellValueFactory(new PropertyValueFactory<Income, Double>("amount"));
    inCategoryCol.setCellValueFactory(new PropertyValueFactory<Income, IncomeCategory>("category"));
    inDescriptionCol.setCellValueFactory(new PropertyValueFactory<Income, String>("description"));
    inRecurringCol.setCellValueFactory(new PropertyValueFactory<Income, Boolean>("recurring"));

    expDateCol.setCellValueFactory(new PropertyValueFactory<Expense, String>("date"));
    expAmountCol.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount"));
    expCategoryCol.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category"));
    expDescriptionCol.setCellValueFactory(new PropertyValueFactory<Expense, String>("description"));
    expRecurringCol.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring"));
  private ObservableList<PieChart.Data> createExpensePieChart() {
    return FXCollections.observableArrayList(
        new Data("Food", expenseRegister.getExpenseByCategory(ExpenseCategory.FOOD).getTotalSum()),
        new Data("Books", expenseRegister.getExpenseByCategory(ExpenseCategory.BOOKS).getTotalSum()),
        new Data("Clothes", expenseRegister.getExpenseByCategory(ExpenseCategory.CLOTHES).getTotalSum()),
        new Data("Other", expenseRegister.getExpenseByCategory(ExpenseCategory.OTHER).getTotalSum())
    );
  private ObservableList<PieChart.Data> createIncomePieChart() {
        new Data("Gift", incomeRegister.getIncomeByCategory(IncomeCategory.GIFT).getTotalSum()),
        new Data("Salary", incomeRegister.getIncomeByCategory(IncomeCategory.SALARY).getTotalSum()),
        new Data("Loans", incomeRegister.getIncomeByCategory(IncomeCategory.STUDENT_LOAN).getTotalSum())
  /**
   * Method for disabling the date picker, yet having its opacity at max.
   */
  private void formatDatePicker() {
    date.setValue(LocalDate.now());
    date.setDisable(true);
    date.setStyle("-fx-opacity: 1");
    date.getEditor().setStyle("-fx-opacity: 1");
  }
  /**
   * Method for handling the adding of new entries in the tableview.
   * @param event A button click on the add button.
   */
  @Override
  public void handleAddBtn(javafx.event.ActionEvent event) {
    int sizeBf = (expenseRegister.getItems().size() + incomeRegister.getItems().size());

    if (event.getSource() == addIncome) {
      handleAddIncome();
    } else if (event.getSource() == addExpense){
      handleAddExpense();
    }

    int sizeAf = (expenseRegister.getItems().size() + incomeRegister.getItems().size());

    if (sizeAf != sizeBf) {
      refreshTableView();
      refreshProgress();
    }
  }

  /**
   * Method for handling the editing of a chosen entry in the tableview.
   *
   * @param event A button click on the edit button.
   */
  @Override
  public void handleEditBtn(javafx.event.ActionEvent event) { 
    System.out.println(event.getSource());
    Income chosenIncome = incomeTableView.getSelectionModel().getSelectedItem();
    Expense chosenExpense = expenseTableView.getSelectionModel().getSelectedItem();

    boolean isEditIncome = event.getSource() == editIncomeMenu;

    boolean isDeleteIncome = event.getSource() == deleteIncomeMenu;

    boolean isEditExpense = event.getSource() == editExpenseMenu;

    boolean isDeleteExpense =  event.getSource() == deleteExpenseMenu;

    System.out.println(chosenIncome);
    System.out.println(chosenExpense);

    if (isEditIncome) {
      handleEditIncome(chosenIncome);
    } else if (isDeleteIncome) {
      handleDeleteIncome(chosenIncome);
    } else if (isEditExpense) {
      handleEditExpense(chosenExpense);
    } else if (isDeleteExpense) {
      handleDeleteExpense(chosenExpense);
    //Updates the tableview and pie chart using the register
    refreshTableView();
  }

  /**
   * Deletes an entry from the tableview, if an entry has been selected. The method brings up a
   * popup window, asking for confirmation for deleting the entry.
   *
   * @param event A button click on the delete button
   */
  @Override
  public void handleDeleteBtn(javafx.event.ActionEvent event) {
  }

  /**
   * Method for synching the register with the tableview. The observable list to which the tableview
   * is set, is being refilled with all the entries in the register, keeping it updated with new
   * changes.
   */
  @Override
  public void refreshTableView() {
    this.income.setAll(incomeRegister.getItems());
    //this.sum.setText(String.valueOf(incomeRegister.getTotalSum()));
    this.expenses.setAll(expenseRegister.getItems());
    //this.sum.setText(String.valueOf(incomeRegister.getTotalSum()));
    this.incomePieChart.setData(createIncomePieChart());
    this.expensePieChart.setData(createExpensePieChart());
  private void refreshProgress() {
    budgetProgress.setProgress(expenseRegister.getTotalSum()/incomeRegister.getTotalSum());
  }

  @FXML
  private void handleAddIncome() {
    //Instantiate FXML loader and loads the popup for adding income
    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(getClass().getResource("/view/AddIncome.fxml"));

    Income newIncome = null;
    String dialogTitle = "Add income";
    // Load the FXML file for your dialog box
    Dialog<Income> dialog = new Dialog<>();
    dialog.initModality(Modality.APPLICATION_MODAL);

    try {
      // Set the Dialog's content to the loaded FXML file
      dialog.getDialogPane().setContent(loader.load());
    } catch (IOException e) {
      e.printStackTrace();
    }

    // Get the controller for the loaded FXML file
    AddIncomeController dialogController = loader.getController();
    //Sets the title of the dialog box
    dialog.setTitle(dialogTitle);
    // Show the Dialog and wait for the user to close it
    dialog.showAndWait();
    //Get the newly created income from the dialog pane
    newIncome = dialogController.getNewIncome();

    //Adds the new item to the register
    if (newIncome != null) {
      incomeRegister.addItem(newIncome);
      incomePieChart.setData(createIncomePieChart());
    }
  }

  @FXML
  private void handleAddExpense() {
    //Instantiate FXML loader and loads the popup for adding expense
    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(getClass().getResource("/view/AddExpense.fxml"));

    Expense newExpense = null;
    String dialogTitle = "Add expense";
    // Load the FXML file for your dialog box
    Dialog<Expense> dialog = new Dialog<>();
    dialog.initModality(Modality.APPLICATION_MODAL);

    try {
      // Set the Dialog's content to the loaded FXML file
      dialog.getDialogPane().setContent(loader.load());
    } catch (IOException e) {
      e.printStackTrace();
    }

    // Get the controller for the loaded FXML file
    AddExpenseController dialogController = loader.getController();

    dialog.setTitle(dialogTitle);
    // Show the Dialog and wait for the user to close it
    dialog.showAndWait();

    //Get the newly created expense from the dialog pane
    newExpense = dialogController.getNewExpense();

    //Adds the new item to the register
    if (newExpense != null) {
      expenseRegister.addItem(newExpense);
      expensePieChart.setData(createExpensePieChart());
    }
  }

  @FXML
  private void handleEditIncome(Income chosenIncome) {
    //Create copy of chosenIncome before changes

    //Instantiate FXML loader and loads the popup for adding income
    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(getClass().getResource("/view/AddIncome.fxml"));

    String dialogTitle = "Edit income";
    // Load the FXML file for your dialog box
    Dialog<Income> dialog = new Dialog<>();
    dialog.initModality(Modality.APPLICATION_MODAL);

    try {
      // Set the Dialog's content to the loaded FXML file
      dialog.getDialogPane().setContent(loader.load());
    } catch (IOException e) {
      e.printStackTrace();
    }

    // Get the controller for the loaded FXML file
    AddIncomeController dialogController = loader.getController();
    //Binds the selected item to another item which is defined in the ItemController
    dialogController.setIncome(chosenIncome);

    dialog.setTitle(dialogTitle);
    // Show the Dialog and wait for the user to close it
    dialog.showAndWait();

    this.incomePieChart.setData(createIncomePieChart());

  }

  @FXML
  private void handleEditExpense(Expense chosenExpense) {
    //Instantiate FXML loader and loads the popup for adding expense
    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(getClass().getResource("/view/AddExpense.fxml"));

    String dialogTitle = "Edit expense";
    // Load the FXML file for your dialog box
    Dialog<Expense> dialog = new Dialog<>();
    dialog.initModality(Modality.APPLICATION_MODAL);

    try {
      // Set the Dialog's content to the loaded FXML file
      dialog.getDialogPane().setContent(loader.load());
    } catch (IOException e) {
      e.printStackTrace();
    }

    // Get the controller for the loaded FXML file
    AddExpenseController dialogController = loader.getController();
    //Binds the selected item to another item which is defined in the ItemController
    dialogController.setExpense(chosenExpense);
    dialog.setTitle(dialogTitle);
    // Show the Dialog and wait for the user to close it
    dialog.showAndWait();
    this.expensePieChart.setData(createExpensePieChart());
  }
  
  @FXML
  private void handleDeleteIncome(Income chosenIncome) {
    Optional<ButtonType> isConfirmed = showConfirmationDialog();
    if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) {
      incomeRegister.removeItem(chosenIncome);
    }
    this.incomePieChart.setData(createIncomePieChart());
  }

  @FXML
  private void handleDeleteExpense(Expense chosenExpense) {
    Optional<ButtonType> isConfirmed = showConfirmationDialog();
    if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) {
      expenseRegister.removeItem(chosenExpense);
    }
    this.expensePieChart.setData(createExpensePieChart());
   * Returns an optional, which is a popup alert box, asking for confirmation for deleting an
   * entry.
   *
   * @return An alert box, asking for confirmation for deleting the selected entry of the tableview.
   */
  @Override
  public Optional<ButtonType> showConfirmationDialog() {
    Alert alert = new Alert(AlertType.CONFIRMATION);
    alert.setTitle("Confirm Delete");
    alert.setHeaderText("Delete Confirmation");
    alert.setContentText("Are you sure you would like to delete the selected entry?");

    return alert.showAndWait();
  }

  /**
   * Saves the changes made to the tableview by writing the information to a file.
   * @throws IOException If an error occurs while writing to the file.
   */
  @Override
  public void saveDataToFile() throws IOException {
    fileHandling.writeItemRegisterToFile(incomeRegister, "Income");
    fileHandling.writeItemRegisterToFile(expenseRegister, "Expense");
  }

  /**
   * Method that either reads data from a file with which it fills an income register, if older changes exist, or instantiates an income register if the file is empty.
   * @param fileName The name of the file that is being read from.
   * @return An object of type IncomeRegister.
   * @throws IOException If an error occurs while reading from the file.
   */
  public IncomeRegister loadIncomeDataFromFile(String fileName) throws IOException {
    //Instantiate new incomeRegister
    if (fileHandling.isEmpty(fileName)) {
      incomeRegister = new IncomeRegister();
    } else { //Load previous income register
      try {
        incomeRegister = fileHandling.readIncomeRegisterFromFile(fileName);
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    return incomeRegister;
  }

  /**
   * Method that either reads data from a file with which it fills an expense register, if older changes exist, or instantiates an expense register if the file is empty.
   * @param fileName The name of the file that is being read from.
   * @return An object of type IncomeRegister.
   * @throws IOException If an error occurs while reading from the file.
   */
  public ExpenseRegister loadExpenseDataFromFile(String fileName) throws IOException {
    //ItemRegister<T extends Item>
    if (fileHandling.isEmpty(fileName)) {
      expenseRegister = new ExpenseRegister();
    } else {
      try {
        expenseRegister = fileHandling.readExpenseRegisterFromFile(fileName);
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    return expenseRegister;
  }

  /**
   * Method that either reads data from a file with which it fills a budget register, if this is an old budget, or instantiates a budget register if this is a new budget.
   * @param fileName The name of the file that is being read from.
   * @return An object of type GeneralBudget.
   * @throws IOException If an error occurs while reading from the file.
   */
  public GeneralBudget loadBudgetDataFromFile(String fileName) throws IOException {
    FileHandlingBudget fileHandlingBudget = new FileHandlingBudget();
    //Instantiate new budget
    if (fileHandlingBudget.isEmpty(fileName)) {
      generalBudget = new GeneralBudget(31, 1000);
    } else { //Load previous budget
      try {
        generalBudget = fileHandlingBudget.readGeneralBudgetFromFile(fileName);
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    return generalBudget;
  }

  /**
   * Switches scenes back to main menu, by loading a new FXML file and setting the scene to this location.
   * @param event A button click on the return to main menu button
   * @throws IOException If an error occurs with loading any of the FXML files.
  public void returnToMainMenu(javafx.event.ActionEvent event) throws IOException {
    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(getClass().getResource("/view/MainMenuNew.fxml"));

    Parent root = loader.load();
    Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
    Scene scene = new Scene(root);
    stage.setScene(scene);
    stage.show();