Skip to content
Snippets Groups Projects
Forked from Surya Bahadur Kathayat / idatt1002
This fork has diverged from the upstream repository.
BudgetController.java 11.00 KiB
package no.ntnu.idatt1002.demo.controller;

import java.time.LocalDate;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
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.*;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.text.Text;
import javafx.stage.Modality;
import javafx.stage.Stage;
import no.ntnu.idatt1002.demo.data.Budget.BudgetItem;
import no.ntnu.idatt1002.demo.data.Budget.FileHandlingBudget;
import no.ntnu.idatt1002.demo.data.Budget.GeneralBudget;
import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory;


import java.io.IOException;
import java.util.Optional;
import no.ntnu.idatt1002.demo.data.Economics.IncomeCategory;

/**
 * Controller for budget scene in the application. This controller manages all actions that relates to the budget tableview (add, edit and delete), the switching
 * of scenes from the budget scene to another scene, and the saving of the table, whenever the user switches to another scene.
 *
 * @author Anders Emil Bergan
 * @since 24.3.2023
 */
public class BudgetController implements FinanceController {

    private GeneralBudget general;

    @FXML
    private Button addBtn;

    @FXML
    private Button editBtn;

    @FXML
    private Button returnBtn;
    @FXML
    private TableColumn<BudgetItem, Double> amountCol;

    @FXML
    private TableView<BudgetItem> budgetTableView = new TableView<>();

    @FXML
    private TableColumn<BudgetItem, ExpenseCategory> categoryCol;

    @FXML
    private TableColumn<BudgetItem, String> descriptionCol;

    @FXML
    private Text sum;

    @FXML
    private DatePicker date;

    @FXML
    private TableColumn<BudgetItem, Double> percentageColumn;

    @FXML
    private ObservableList<BudgetItem> budgetList;


    /**
     * Initializes the budget register, the observable budget list and the tableview, along with the values of the dropbox used for filtering the tableview.
     * @throws IOException If there occurs any exception when loading the budget register from a file.
     */

    @FXML
    public void initialize() throws IOException {
        //TODO if budget is not empty - disable
        //Initialize table columns
        categoryCol.setCellValueFactory(new PropertyValueFactory<BudgetItem, ExpenseCategory>("budgetCategory"));
        amountCol.setCellValueFactory(new PropertyValueFactory<BudgetItem, Double>("budgetAmount"));
        descriptionCol.setCellValueFactory(new PropertyValueFactory<BudgetItem, String>("budgetDescription"));

        //Initialize registers and tableview
        general = loadBudgetDataFromFile("Budget");
        budgetList = FXCollections.observableArrayList(general.getBudgetItems());
        budgetTableView.setItems(budgetList);

        formatDatePicker();
        //createBudgetPieChart();
        //Initialize sum field under the tableview
        //sum.setText(String.valueOf(general.totalSum()));
    }

    private ObservableList<PieChart.Data> createBudgetPieChart() { //TODO DOESNT WORK IF BUDGETITEM HAS NO BUDGET
        return FXCollections.observableArrayList(
            new Data("Food", general.getBudgetItem(ExpenseCategory.FOOD).getBudgetAmount()),
            new Data("Books", general.getBudgetItem(ExpenseCategory.BOOKS).getBudgetAmount()),
            new Data("Clothes", general.getBudgetItem(ExpenseCategory.CLOTHES).getBudgetAmount()),
            new Data("Other", general.getBudgetItem(ExpenseCategory.OTHER).getBudgetAmount())
        );
    }

    /**
     * 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");
    }

    @Override
    public void handleAddBtn(ActionEvent event) {
        handleEditBtn(event);
    }
    /**
     * Adds or edits a budget item, depending on what mode the DialogMode enum is at. The method brings up a dialog box popup in which the user can fill and choose
     * values that the budget item will have. Open exiting the popup, the changes the saved to the tableview.
     * @param event A button click on either the add or delete button.
     */
    @Override
    public void handleEditBtn(ActionEvent event) {
        BudgetItem item = null;
        String dialogTitle = "";
        DialogMode dialogMode;

        FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/AddBudget.fxml"));
        Dialog<BudgetItem> dialog = new Dialog<>();
        dialog.initModality(Modality.APPLICATION_MODAL);

        //Try to load the FXML file onto another dialogPane
        try{
            dialog.getDialogPane().setContent(loader.load());
        } catch(IOException e) {
            e.printStackTrace();
        }

        //Loads the controller for the dialog box that appears whenever one adds or edits a budget item
        AddBudgetController budgetController = loader.getController();

        //Sets the title of the dialog box
        if(event.getSource().equals(addBtn)){
            dialogMode = DialogMode.ADD;
            dialogTitle = "New Budget";
        }
        else if (event.getSource().equals(editBtn) && budgetTableView.getSelectionModel().getSelectedItem() != null) {
            dialogMode = DialogMode.EDIT;
            dialogTitle = "Edit expense";
            //Gets the selected item from the table
            item = budgetTableView.getSelectionModel().getSelectedItem();
            //Binds the selected item to another item which is defined in the budgetcontroller
            budgetController.setBudget(item);
        } else {
            return;
        }

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

        //Adds the new item to the register
        item = budgetController.getNewBudgetItem();
        if(item != null && dialogMode == DialogMode.ADD){
            try {
            general.addToBudgetBudgetItem(item);
            } catch(IllegalArgumentException e) {
                showIllegalBudgetItemDialog();
            }
        }
        //Updates the tableview 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
     */
    @FXML
    public void handleDeleteBtn(ActionEvent event) {
        //Gets the selected item from the tableview
        BudgetItem item = budgetTableView.getSelectionModel().getSelectedItem();
        //Exits the method if nothing is selected
        if (item == null) {
            return;
        }
        //Brings up a confirmation popup
        Optional<ButtonType> isConfirmed = showConfirmationDialog();
        if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) {
            general.deleteItemFromBudget(item.getBudgetCategory());
            refreshTableView();
        }
    }

    /**
     * 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.
     */
    public void refreshTableView(){
        this.budgetList.setAll(general.getBudgetItems());
        //Refreshing the sum of the amounts of the budget
        //this.sum.setText(String.valueOf(general.totalSum()));
    }

    /**
     * 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(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 {
        FileHandlingBudget fileHandlingBudget = new FileHandlingBudget();
        fileHandlingBudget.writeGeneralBudgetToFile("Budget", general);
    }

    /**
     * Returns an optional, which is a popup alert box, informing that either the budget amount has
     * been exceeded or that the same category has been entered twice in the budget tableview.
     */
    private void showIllegalBudgetItemDialog() {
        Alert alert = new Alert(AlertType.ERROR);
        alert.setTitle("Budget amount exceeded/Category already exists");
        alert.setHeaderText("Your budget exceeds the max limit OR a budget item of the same category already exists in the table");
        alert.setContentText("The total budget sum must be below " + general.getMaxAmount() + " OR Each category can only have one entry in the budget table");
        alert.showAndWait();
    }

    /**
     * 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)) {
            general = new GeneralBudget(31, 1000);
        } else { //Load previous budget
            try {
                general = fileHandlingBudget.readGeneralBudgetFromFile(fileName);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return general;
    }

    /**
     * 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.
     */
   @FXML
    public void returnToMainMenu(ActionEvent event) throws IOException {
       //Always saving the data when switching scenes
        saveDataToFile();
        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();
    }
}