Newer
Older
Harry Linrui XU
committed
package no.ntnu.idatt1002.demo.controller;
import java.io.IOException;
Harry Linrui XU
committed
import java.time.LocalDate;
Harry Linrui XU
committed
import java.util.Optional;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
Harry Linrui XU
committed
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;
Harry Linrui XU
committed
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ComboBox;
import javafx.scene.control.DatePicker;
Harry Linrui XU
committed
import javafx.scene.control.Dialog;
Harry Linrui XU
committed
import javafx.scene.control.Label;
Harry Linrui XU
committed
import javafx.scene.control.MenuItem;
Harry Linrui XU
committed
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
Harry Linrui XU
committed
import javafx.stage.Modality;
Harry Linrui XU
committed
import javafx.stage.Stage;
Harry Linrui XU
committed
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;
/**
* Class for representing an overview of the income and expenses of the users budget.
* Displays information in tables and pie charts. It is possible to add, edit and delete
* income and expenses. The difference of the expense and income sum contribute to
* the monthly budget progress.
* @author Harry Linrui Xu
* @since 30.03.2023
*/
Harry Linrui XU
committed
public class IncomeExpenseController extends FinanceController {
private final static String sumText = "Sum: ";
Harry Linrui XU
committed
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
@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 Label inSum;
Harry Linrui XU
committed
@FXML
private Label expSum;
Harry Linrui XU
committed
@FXML
Harry Linrui XU
committed
private MenuItem addExpense;
@FXML
private MenuItem addIncome;
Harry Linrui XU
committed
@FXML
private DatePicker date;
@FXML
private Label daysLeftLbl;
@FXML
private ComboBox<?> filter;
@FXML
private Button returnBtn;
Harry Linrui XU
committed
@FXML
private Button continueBtn;
Harry Linrui XU
committed
@FXML
private Label title;
@FXML
private Label errorMsg;
@FXML
private MenuItem editIncomeMenu;
@FXML
private MenuItem deleteIncomeMenu;
@FXML
private MenuItem editExpenseMenu;
@FXML
private MenuItem deleteExpenseMenu;
Harry Linrui XU
committed
private IncomeRegister incomeRegister;
private ExpenseRegister expenseRegister;
private GeneralBudget generalBudget;
private ObservableList<Income> income;
private ObservableList<Expense> expenses;
Harry Linrui XU
committed
@FXML
private PieChart expensePieChart;
@FXML
private PieChart incomePieChart;
Harry Linrui XU
committed
@FXML
public void initialize() {
Harry Linrui XU
committed
System.out.println("Start of initialize");
Harry Linrui XU
committed
//Initialize columns
Harry Linrui XU
committed
setColumns();
Harry Linrui XU
committed
//Initialize registers and tableview
incomeRegister = loadIncomeDataFromFile("Income");
income = FXCollections.observableArrayList(incomeRegister.getItems());
incomeTableView.setItems(income);
System.out.println("After income rgister ");
Harry Linrui XU
committed
expenseRegister = loadExpenseDataFromFile("Expense");
expenses = FXCollections.observableArrayList(expenseRegister.getItems());
expenseTableView.setItems(expenses);
Harry Linrui XU
committed
System.out.println("Afte expense register");
Harry Linrui XU
committed
//Setting pie chart values to correspond with the registers
incomePieChart.setLegendSide(Side.RIGHT);
incomePieChart.setLabelLineLength(10);
Harry Linrui XU
committed
Harry Linrui XU
committed
expensePieChart.setLegendSide(Side.RIGHT);
expensePieChart.setLabelLineLength(10);
Harry Linrui XU
committed
Harry Linrui XU
committed
refreshPieChart();
Harry Linrui XU
committed
formatDatePicker();
Harry Linrui XU
committed
//Initialize sum field under the tableview
inSum.setText(sumText + String.valueOf(incomeRegister.getTotalSum()));
expSum.setText(sumText + String.valueOf(expenseRegister.getTotalSum()));
Harry Linrui XU
committed
}
private void setColumns() {
inDateCol.setCellValueFactory(new PropertyValueFactory<>("date"));
inAmountCol.setCellValueFactory(new PropertyValueFactory<>("amount"));
inCategoryCol.setCellValueFactory(new PropertyValueFactory<>("category"));
inDescriptionCol.setCellValueFactory(new PropertyValueFactory<>("description"));
inRecurringCol.setCellValueFactory(new PropertyValueFactory<>("recurring"));
expDateCol.setCellValueFactory(new PropertyValueFactory<>("date"));
expAmountCol.setCellValueFactory(new PropertyValueFactory<>("amount"));
expCategoryCol.setCellValueFactory(new PropertyValueFactory<>("category"));
expDescriptionCol.setCellValueFactory(new PropertyValueFactory<>("description"));
expRecurringCol.setCellValueFactory(new PropertyValueFactory<>("recurring"));
Harry Linrui XU
committed
}
Harry Linrui XU
committed
Harry Linrui XU
committed
public ObservableList<PieChart.Data> createExpensePieChart() {
Harry Linrui XU
committed
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())
);
Harry Linrui XU
committed
}
Harry Linrui XU
committed
Harry Linrui XU
committed
private ObservableList<PieChart.Data> createIncomePieChart() {
Harry Linrui XU
committed
return FXCollections.observableArrayList(
Harry Linrui XU
committed
new Data("Gift", incomeRegister.getIncomeByCategory(IncomeCategory.GIFT).getTotalSum()),
Harry Linrui XU
committed
new Data("Salary", incomeRegister.getIncomeByCategory(IncomeCategory.SALARY).getTotalSum()),
Harry Linrui XU
committed
new Data("Loans", incomeRegister.getIncomeByCategory(IncomeCategory.STUDENT_LOAN).getTotalSum())
Harry Linrui XU
committed
);
Harry Linrui XU
committed
}
Harry Linrui XU
committed
/**
* Method for disabling the date picker, yet having its opacity at max.
*/
Harry Linrui XU
committed
@Override
public void formatDatePicker() {
Harry Linrui XU
committed
date.setValue(LocalDate.now());
date.setDisable(true);
date.setStyle("-fx-opacity: 1");
date.getEditor().setStyle("-fx-opacity: 1");
}
Harry Linrui XU
committed
/**
* Method for handling the adding of new entries in the tableview.
* @param event A button click on the add button.
*/
Harry Linrui XU
committed
Harry Linrui XU
committed
@Override
Harry Linrui XU
committed
public void handleAddBtn(javafx.event.ActionEvent event) {
int sizeBf = (expenseRegister.getItems().size() + incomeRegister.getItems().size());
Harry Linrui XU
committed
if (event.getSource() == addIncome) {
handleAddIncome();
} else if (event.getSource() == addExpense){
handleAddExpense();
}
int sizeAf = (expenseRegister.getItems().size() + incomeRegister.getItems().size());
if (sizeAf != sizeBf) {
refreshTableView();
}
Harry Linrui XU
committed
}
/**
* 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) {
Harry Linrui XU
committed
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;
if (isEditIncome) {
handleEditIncome(chosenIncome);
} else if (isDeleteIncome) {
handleDeleteIncome(chosenIncome);
} else if (isEditExpense) {
handleEditExpense(chosenExpense);
} else if (isDeleteExpense) {
handleDeleteExpense(chosenExpense);
Harry Linrui XU
committed
//Updates the tableview and pie chart using the register
refreshTableView();
Harry Linrui XU
committed
}
/**
* 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
Harry Linrui XU
committed
public void handleDeleteBtn(javafx.event.ActionEvent event) {
Harry Linrui XU
committed
handleEditBtn(event);
Harry Linrui XU
committed
}
/**
* 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() {
Harry Linrui XU
committed
this.income.setAll(incomeRegister.getItems());
this.inSum.setText("Sum: " + String.valueOf(incomeRegister.getTotalSum()));
Harry Linrui XU
committed
Harry Linrui XU
committed
this.expenses.setAll(expenseRegister.getItems());
this.expSum.setText("Sum: " + String.valueOf(expenseRegister.getTotalSum()));
Harry Linrui XU
committed
}
Harry Linrui XU
committed
@Override
public void refreshPieChart() {
Harry Linrui XU
committed
this.incomePieChart.setData(createIncomePieChart());
this.expensePieChart.setData(createExpensePieChart());
Harry Linrui XU
committed
}
Harry Linrui XU
committed
Harry Linrui XU
committed
@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"));
Harry Linrui XU
committed
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) {
showErrorDialogBox("Loading", "Error in loading dialog box", "Could not load"
+ "the AddIncome window");
Harry Linrui XU
committed
}
// 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());
Harry Linrui XU
committed
}
}
@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"));
Harry Linrui XU
committed
String dialogTitle = "Add expense";
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) {
showErrorDialogBox("Loading", "Error in loading dialog box", "Could not load"
+ "the AddExpense window");
Harry Linrui XU
committed
}
// 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());
Harry Linrui XU
committed
}
}
@FXML
private void handleEditIncome(Income chosenIncome) {
//Create copy of chosenIncome before changes
Harry Linrui XU
committed
//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) {
showErrorDialogBox("Loading", "Error in loading dialog box", "Could not load"
+ "the EditIncome window");
Harry Linrui XU
committed
}
// 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();
incomePieChart.setData(createIncomePieChart());
Harry Linrui XU
committed
}
@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) {
showErrorDialogBox("Loading", "Error in loading dialog box", "Could not"
+ "load the EditExpense window");
Harry Linrui XU
committed
}
// 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());
Harry Linrui XU
committed
}
@FXML
private void handleDeleteIncome(Income chosenIncome) {
String title = "Confirm Delete" ;
String header = "Delete Confirmation";
String content = "Are you sure you would like to delete the selected income?";
Optional<ButtonType> isConfirmed = showConfirmationDialog(title, header, content);
Harry Linrui XU
committed
if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) {
incomeRegister.removeItem(chosenIncome);
}
this.incomePieChart.setData(createIncomePieChart());
Harry Linrui XU
committed
}
@FXML
private void handleDeleteExpense(Expense chosenExpense) {
String title = "Confirm Delete" ;
String header = "Delete Confirmation";
String content = "Are you sure you would like to delete the selected expense?";
Optional<ButtonType> isConfirmed = showConfirmationDialog(title, header, content);
Harry Linrui XU
committed
if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) {
expenseRegister.removeItem(chosenExpense);
}
this.expensePieChart.setData(createExpensePieChart());
Harry Linrui XU
committed
}
Harry Linrui XU
committed
/**
* 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
Harry Linrui XU
committed
public void saveDataToFile() throws IOException {
FileHandling.writeItemRegisterToFile(incomeRegister, "Income");
FileHandling.writeItemRegisterToFile(expenseRegister, "Expense");
Harry Linrui XU
committed
}
public void saveDisposableIncomeToFile() throws IOException {
String disposableIncomeAsString = String.valueOf(incomeRegister.getTotalSum() - expenseRegister.getTotalSum());
Harry Linrui XU
committed
FileHandlingBudget.writeMaxAmountToFile(FileHandlingBudget.readSelectedBudget(), disposableIncomeAsString);
Harry Linrui XU
committed
/**
* 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
Harry Linrui XU
committed
*/
Harry Linrui XU
committed
@FXML
public void switchScene(javafx.event.ActionEvent event) {
try {
FXMLLoader loader = new FXMLLoader();
Harry Linrui XU
committed
if (event.getSource() == returnBtn) {
loader.setLocation(getClass().getResource("/view/MainMenuNew.fxml"));
Harry Linrui XU
committed
} else if (event.getSource() == continueBtn) {
loader.setLocation(getClass().getResource("/view/newBudgetBudgert.fxml"));
} else if (event.getSource() == null) {
Harry Linrui XU
committed
FileHandlingBudget.deleteBudgetDirectory(FileHandlingBudget.readSelectedBudget());
//removeBudgetNameFromArchive
FileHandlingBudget.updateSelectedBudget("");
Harry Linrui XU
committed
}
Parent root = loader.load();
Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
} catch(IOException ioe) {
Harry Linrui XU
committed
showErrorDialogBox("Loading error", "Error in loading", "Could not save"