diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java index 1325d2968e40ab23d687559dc589f1b6a60d8a79..3d33f030565c21a0aaf643e6aa699b38a467c732 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java @@ -1,6 +1,9 @@ package no.ntnu.idatt1002.demo.controller; import java.time.LocalDate; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; @@ -20,12 +23,11 @@ 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.Expense; 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 @@ -44,6 +46,9 @@ public class BudgetController implements FinanceController { @FXML private Button editBtn; + @FXML + private Button deleteBtn; + @FXML private Button returnBtn; @FXML @@ -70,16 +75,16 @@ public class BudgetController implements FinanceController { @FXML private ObservableList<BudgetItem> budgetList; + @FXML + private PieChart budgetPieChart; + /** * 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 (HAS VALUES) -> make uneditable -> EVENT FILTER TO CONTEXT MENU - //TODO disable return to main menu when creating budget because this is the same view as when you create budeget + public void initialize() { //TODO make budget item throw exception with negative amount //TODO specify error messgage for when amount is exceeded / duplicate exists //todo properly close screen so things are saved @@ -88,31 +93,59 @@ public class BudgetController implements FinanceController { 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); + try { //Initialize registers, tableview and pie charts + //if (FileHandlingBudget.hasBudgetSet - from line 4/5, so after the general amount and such) { + //add, edit and delete buttons are removed. + general = loadBudgetDataFromFile("Budget"); + budgetList = FXCollections.observableArrayList(general.getBudgetItems()); + budgetTableView.setItems(budgetList); + refreshPieChart(); + } catch(IOException ioe) { + showErrorDialogBox("File reading error", "Error in reading from file", "An error occurred" + + "when reading the registers from file"); + } + setButtons(); formatDatePicker(); - //createBudgetPieChart(); + //Initialize sum field under the tableview //sum.setText(String.valueOf(general.totalSum())); } - private ObservableList<PieChart.Data> createBudgetPieChart() throws IllegalArgumentException { //TODO DOESNT WORK IF BUDGETITEM HAS NO BUDGET + private void setButtons() { + FileHandlingBudget fileHandlingBudget = new FileHandlingBudget(); try { - 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()) - ); - } catch(IllegalArgumentException iae) { - return FXCollections.observableArrayList(); + if (fileHandlingBudget.isEmpty("Budget")) { + returnBtn.setOpacity(0); + } else { + addBtn.setDisable(true); + editBtn.setDisable(true); + deleteBtn.setDisable(true); + } + + } catch(IOException ioe) { + 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?"); + + alert.show(); } } + private ObservableList<PieChart.Data> createBudgetPieChart() { // + 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()) + ); + } + + private void refreshPieChart() { + this.budgetPieChart.setData(createBudgetPieChart()); + } + /** * Method for disabling the date picker, yet having its opacity at max. */ @@ -146,7 +179,8 @@ public class BudgetController implements FinanceController { try{ dialog.getDialogPane().setContent(loader.load()); } catch(IOException e) { - e.printStackTrace(); + showErrorDialogBox("Loading error", "Error in loading dialog", "An error occurred" + + "when loading the AddBudget window"); } //Loads the controller for the dialog box that appears whenever one adds or edits a budget item @@ -176,13 +210,17 @@ public class BudgetController implements FinanceController { item = budgetController.getNewBudgetItem(); if(item != null && dialogMode == DialogMode.ADD){ try { - general.addToBudgetBudgetItem(item); + general.addToBudgetBudgetItem(item); } catch(IllegalArgumentException e) { - showIllegalBudgetItemDialog(); + String title = "Budget amount exceeded/Category already exists"; + String header = "Your budget exceeds the max limit OR a budget item of the same category already exists in the table"; + String content = "The total budget sum must be below " + general.getMaxAmount() + " OR Each category can only have one entry in the budget table"; + showErrorDialogBox(title, header, content); } } //Updates the tableview using the register refreshTableView(); + refreshPieChart(); } @@ -199,10 +237,14 @@ public class BudgetController implements FinanceController { return; } //Brings up a confirmation popup - Optional<ButtonType> isConfirmed = showConfirmationDialog(); + String title = "Confirm Delete" ; + String header = "Delete Confirmation"; + String content = "Are you sure you would like to delete the selected entry?"; + Optional<ButtonType> isConfirmed = showConfirmationDialog(title, header, content); if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { general.deleteItemFromBudget(item.getBudgetCategory()); refreshTableView(); + refreshPieChart(); } } @@ -221,7 +263,7 @@ public class BudgetController implements FinanceController { * @return An alert box, asking for confirmation for deleting the selected entry of the tableview. */ @Override - public Optional<ButtonType> showConfirmationDialog() { + public Optional<ButtonType> showConfirmationDialog(String title, String header, String content) { Alert alert = new Alert(Alert.AlertType.CONFIRMATION); alert.setTitle("Confirm Delete"); alert.setHeaderText("Delete Confirmation"); @@ -242,14 +284,13 @@ public class BudgetController implements FinanceController { } /** - * 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. + * Displays an alert box of type error, informing of a custom error. */ - private void showIllegalBudgetItemDialog() { + private void showErrorDialogBox(String title, String header, String content) { 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.setTitle(title); + alert.setHeaderText(header); + alert.setContentText(content); alert.showAndWait(); } @@ -269,7 +310,8 @@ public class BudgetController implements FinanceController { try { general = fileHandlingBudget.readGeneralBudgetFromFile(fileName); } catch (IOException e) { - e.printStackTrace(); + showErrorDialogBox("File error", "Error in reading from fil", "An error occurred" + + "when reading GeneralBudget from the file"); } } return general; @@ -280,9 +322,11 @@ public class BudgetController implements FinanceController { * @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 + @FXML public void returnToMainMenu(ActionEvent event) throws IOException { - //Always saving the data when switching scenes + //Adds unused categories to the table + general.addUnusedCategories(); + //Always saving the data when switching scenes saveDataToFile(); FXMLLoader loader = new FXMLLoader(); loader.setLocation(getClass().getResource("/view/MainMenuNew.fxml")); diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/FinanceController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/FinanceController.java index 09b609371ec64d1bad41ef3fe070846f2a1edc17..4e63594465fe17a9985f94c7274956aaa7e40064 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/FinanceController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/FinanceController.java @@ -42,7 +42,7 @@ public interface FinanceController { * 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. */ - Optional<ButtonType> showConfirmationDialog(); + Optional<ButtonType> showConfirmationDialog(String title, String header, String content); /** * Saves the changes made to the tableview by writing the information to a file. diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeExpenseController.java index 4e9f1bc77f02134b0d3e7da8602d6ffbc7737e9e..27996dca15ac873a7e1b27289a6be97ae769e1c9 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeExpenseController.java @@ -1,6 +1,5 @@ package no.ntnu.idatt1002.demo.controller; -import java.awt.event.ActionEvent; import java.io.IOException; import java.time.LocalDate; import java.util.Optional; @@ -27,7 +26,6 @@ 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 javafx.stage.Modality; import javafx.stage.Stage; import no.ntnu.idatt1002.demo.data.Budget.FileHandlingBudget; @@ -40,8 +38,17 @@ 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 { +/** + * 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 + */ +public class IncomeExpenseController implements FinanceController { + private final static String sumText = "Sum: "; @FXML private TableColumn<Expense, Double> expAmountCol; @@ -79,10 +86,10 @@ public class IncomeExpenseController implements FinanceController { private TableView<Income> incomeTableView; @FXML - private Text inSum; + private Label inSum; @FXML - private Text expSum; + private Label expSum; @FXML private MenuItem addExpense; @@ -108,6 +115,9 @@ public class IncomeExpenseController implements FinanceController { @FXML private Label title; + @FXML + private Label errorMsg; + @FXML private MenuItem editIncomeMenu; @@ -136,51 +146,57 @@ public class IncomeExpenseController implements FinanceController { @FXML private PieChart incomePieChart; + FileHandling fileHandling; @FXML - public void initialize() throws IOException { + public void initialize() { fileHandling = new FileHandling(); //Initialize columns 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); + //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 - incomePieChart.setLegendSide(Side.RIGHT); + //Setting pie chart values to correspond with the registers + incomePieChart.setLegendSide(Side.RIGHT); - expensePieChart.setLegendSide(Side.RIGHT); - expensePieChart.setLabelLineLength(10); + expensePieChart.setLegendSide(Side.RIGHT); + expensePieChart.setLabelLineLength(10); + + refreshPieCharts(); refreshPieCharts(); refreshProgress(); formatDatePicker(); + //Initialize sum field under the tableview - // inSum.setText(String.valueOf(incomeRegister.getTotalSum())); - //expSum.setText(String.valueOf(expenseRegister.getTotalSum())); + inSum.setText(sumText + String.valueOf(incomeRegister.getTotalSum())); + expSum.setText(sumText + 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")); + 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")); } private ObservableList<PieChart.Data> createExpensePieChart() { @@ -250,9 +266,6 @@ public class IncomeExpenseController implements FinanceController { boolean isDeleteExpense = event.getSource() == deleteExpenseMenu; - System.out.println(chosenIncome); - System.out.println(chosenExpense); - if (isEditIncome) { handleEditIncome(chosenIncome); } else if (isDeleteIncome) { @@ -287,10 +300,10 @@ public class IncomeExpenseController implements FinanceController { @Override public void refreshTableView() { this.income.setAll(incomeRegister.getItems()); - //this.sum.setText(String.valueOf(incomeRegister.getTotalSum())); + this.inSum.setText("Sum: " + String.valueOf(incomeRegister.getTotalSum())); this.expenses.setAll(expenseRegister.getItems()); - //this.sum.setText(String.valueOf(incomeRegister.getTotalSum())); + this.expSum.setText("Sum: " + String.valueOf(expenseRegister.getTotalSum())); } private void refreshPieCharts() { @@ -308,7 +321,7 @@ public class IncomeExpenseController implements FinanceController { FXMLLoader loader = new FXMLLoader(); loader.setLocation(getClass().getResource("/view/AddIncome.fxml")); - Income newIncome = null; + Income newIncome; String dialogTitle = "Add income"; // Load the FXML file for your dialog box Dialog<Income> dialog = new Dialog<>(); @@ -318,7 +331,8 @@ public class IncomeExpenseController implements FinanceController { // Set the Dialog's content to the loaded FXML file dialog.getDialogPane().setContent(loader.load()); } catch (IOException e) { - e.printStackTrace(); + showErrorDialogBox("Loading", "Error in loading dialog box", "Could not load" + + "the AddIncome window"); } // Get the controller for the loaded FXML file @@ -343,7 +357,7 @@ public class IncomeExpenseController implements FinanceController { FXMLLoader loader = new FXMLLoader(); loader.setLocation(getClass().getResource("/view/AddExpense.fxml")); - Expense newExpense = null; + Expense newExpense; String dialogTitle = "Add expense"; // Load the FXML file for your dialog box Dialog<Expense> dialog = new Dialog<>(); @@ -353,7 +367,8 @@ public class IncomeExpenseController implements FinanceController { // Set the Dialog's content to the loaded FXML file dialog.getDialogPane().setContent(loader.load()); } catch (IOException e) { - e.printStackTrace(); + showErrorDialogBox("Loading", "Error in loading dialog box", "Could not load" + + "the AddExpense window"); } // Get the controller for the loaded FXML file @@ -390,7 +405,8 @@ public class IncomeExpenseController implements FinanceController { // Set the Dialog's content to the loaded FXML file dialog.getDialogPane().setContent(loader.load()); } catch (IOException e) { - e.printStackTrace(); + showErrorDialogBox("Loading", "Error in loading dialog box", "Could not load" + + "the EditIncome window"); } // Get the controller for the loaded FXML file @@ -402,7 +418,7 @@ public class IncomeExpenseController implements FinanceController { // Show the Dialog and wait for the user to close it dialog.showAndWait(); - this.incomePieChart.setData(createIncomePieChart()); + incomePieChart.setData(createIncomePieChart()); } @@ -421,7 +437,8 @@ public class IncomeExpenseController implements FinanceController { // Set the Dialog's content to the loaded FXML file dialog.getDialogPane().setContent(loader.load()); } catch (IOException e) { - e.printStackTrace(); + showErrorDialogBox("Loading", "Error in loading dialog box", "Could not" + + "load the EditExpense window"); } // Get the controller for the loaded FXML file @@ -437,7 +454,10 @@ public class IncomeExpenseController implements FinanceController { @FXML private void handleDeleteIncome(Income chosenIncome) { - Optional<ButtonType> isConfirmed = showConfirmationDialog(); + 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); if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { incomeRegister.removeItem(chosenIncome); } @@ -446,7 +466,10 @@ public class IncomeExpenseController implements FinanceController { @FXML private void handleDeleteExpense(Expense chosenExpense) { - Optional<ButtonType> isConfirmed = showConfirmationDialog(); + 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); if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { expenseRegister.removeItem(chosenExpense); } @@ -459,7 +482,7 @@ public class IncomeExpenseController implements FinanceController { * @return An alert box, asking for confirmation for deleting the selected entry of the tableview. */ @Override - public Optional<ButtonType> showConfirmationDialog() { + public Optional<ButtonType> showConfirmationDialog(String title, String header, String content) { Alert alert = new Alert(AlertType.CONFIRMATION); alert.setTitle("Confirm Delete"); alert.setHeaderText("Delete Confirmation"); @@ -468,6 +491,19 @@ public class IncomeExpenseController implements FinanceController { return alert.showAndWait(); } + + /** + * Displays an alert box of type error, informing of a custom error. + */ + private void showErrorDialogBox(String title, String header, String content) { + Alert alert = new Alert(AlertType.ERROR); + alert.setTitle(title); + alert.setHeaderText(header); + alert.setContentText(content); + 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. @@ -478,22 +514,23 @@ public class IncomeExpenseController implements FinanceController { 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 { + public IncomeRegister loadIncomeDataFromFile(String fileName) { //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(); + try { + if (fileHandling.isEmpty(fileName)) { + incomeRegister = new IncomeRegister(); + } else { //Load previous income register + incomeRegister = fileHandling.readIncomeRegisterFromFile(fileName); } + } catch (IOException ioe) { + showErrorDialogBox("File reading error", "Error in reading from file", "Could not" + + "read the IncomeRegister from file"); } return incomeRegister; } @@ -502,18 +539,18 @@ public class IncomeExpenseController implements FinanceController { * 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 { + public ExpenseRegister loadExpenseDataFromFile(String fileName) { //ItemRegister<T extends Item> - if (fileHandling.isEmpty(fileName)) { - expenseRegister = new ExpenseRegister(); - } else { - try { + try { + if (fileHandling.isEmpty(fileName)) { + expenseRegister = new ExpenseRegister(); + } else { //Load previous income register expenseRegister = fileHandling.readExpenseRegisterFromFile(fileName); - } catch (IOException e) { - e.printStackTrace(); } + } catch (IOException ioe) { + showErrorDialogBox("File reading error", "Error in reading from file", "Could not" + + "read the ExpenseRegister from file"); } return expenseRegister; } @@ -522,19 +559,19 @@ public class IncomeExpenseController implements FinanceController { * 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 { + public GeneralBudget loadBudgetDataFromFile(String fileName) { FileHandlingBudget fileHandlingBudget = new FileHandlingBudget(); //Instantiate new budget - if (fileHandlingBudget.isEmpty(fileName)) { - generalBudget = new GeneralBudget(31, 1000); - } else { //Load previous budget - try { + try { + if (fileHandling.isEmpty(fileName)) { + generalBudget = new GeneralBudget(31, 1000); + } else { //Load previous income register generalBudget = fileHandlingBudget.readGeneralBudgetFromFile(fileName); - } catch (IOException e) { - e.printStackTrace(); } + } catch (IOException ioe) { + showErrorDialogBox("File reading error", "Error in reading from file", "Could not" + + "read the GeneralBudget from file"); } return generalBudget; } @@ -542,18 +579,23 @@ public class IncomeExpenseController implements FinanceController { /** * 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(javafx.event.ActionEvent event) throws IOException { - saveDataToFile(); - FXMLLoader loader = new FXMLLoader(); - loader.setLocation(getClass().getResource("/view/MainMenuNew.fxml")); + public void returnToMainMenu(javafx.event.ActionEvent event) { + try { + 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(); + } catch(IOException ioe) { + showErrorDialogBox("Loading error", "Error in loading", "Could not save" + + "to file"); + } - Parent root = loader.load(); - Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); - Scene scene = new Scene(root); - stage.setScene(scene); - stage.show(); } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenu.java b/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenu.java index a93d613ee4b1897a8b8083a6ef16a02ccf46eb17..7e37ae10765388d4b137cb627a2c19130365e49e 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenu.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenu.java @@ -2,13 +2,18 @@ package no.ntnu.idatt1002.demo.controller; import java.io.IOException; import java.time.LocalDate; +import java.util.Arrays; +import java.util.List; 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.control.Alert; +import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Button; import javafx.scene.control.DatePicker; import javafx.scene.control.Label; @@ -18,8 +23,11 @@ import javafx.scene.paint.Color; import javafx.stage.Stage; 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 no.ntnu.idatt1002.demo.data.Economics.ExpenseRegister; +import no.ntnu.idatt1002.demo.data.Economics.FileHandling; import no.ntnu.idatt1002.demo.data.Economics.IncomeRegister; +import no.ntnu.idatt1002.demo.data.Economics.Overview; public class MainMenu { @@ -88,56 +96,132 @@ public class MainMenu { IncomeRegister incomeRegister; + FileHandling fileHandling; + + Overview overview; + @FXML - public void initialize() throws IOException { //TODO NEED TO SET STATUSBAR VALUES - //Initialize all controllers - initializeControllers(); + public void initialize() { + fileHandling = new FileHandling(); + //Initialize all registers + overview + try { + incomeRegister = loadIncomeDataFromFile("Income"); + expenseRegister = loadExpenseDataFromFile("Expense"); + generalBudget = loadBudgetDataFromFile("Budget"); + } catch (IOException ioe) { + Alert alert = new Alert(AlertType.ERROR); + alert.setTitle("Could not load register data"); + alert.setHeaderText("Could not load register data"); + alert.setContentText("There was an error loading in the registers"); + + alert.showAndWait(); + } + overview = new Overview(incomeRegister, expenseRegister, generalBudget); mainBar.setStyle("-fx-accent: green;"); - double incomeSum = incomeRegister.getTotalSum(); - double expenseSum = expenseRegister.getTotalSum(); + refreshProgressBars(); + refreshLabels(); + //Set progress when opening scene - //Set progress - mainBar.setProgress(expenseSum/incomeSum); + double maxAmount = generalBudget.getMaxAmount(); + double expenseSum = expenseRegister.getTotalSum(); //Displaying month title.setText("BUDGET " + (LocalDate.now().getMonth())); - double balance = incomeSum - expenseSum; + double balance = maxAmount - expenseSum; //Set balance balanceLbl.setText("Balance: " + (balance)); //Displaying how much of the monthly budget has been spent. - usageLbl.setText("Used " + expenseSum + " out of " + 1000); + usageLbl.setText("Used " + expenseSum + " out of " + generalBudget.getMaxAmount()); if (balance < 0) { balanceLbl.setTextFill(Color.RED); } - //Make calendar uneditable formatDatePicker(); daysLeftLbl.setText("Days left: 31"); //date.restrict } + private void refreshLabels() { + + } + /** - * Method for initializing all controllers in order to load all necessary data for this screen. - * Made this a separate method in order to make initialize() less cluttered. - * @throws IOException If there is an error with reading the files. + * Sets the progress of the progress bars to their most updated data. */ - private void initializeControllers() throws IOException { - //Instantiate income controller and register - IncomeController incomeController = new IncomeController(); - incomeRegister = incomeController.loadIncomeDataFromFile("Income"); - - //Instantiate expense controller and register - ExpensesController expensesController = new ExpensesController(); - expenseRegister = expensesController.loadExpenseDataFromFile("Expense"); - - //Instantiate budget controller and register - BudgetController budgetController = new BudgetController(); - generalBudget = budgetController.loadBudgetDataFromFile("Budget"); + private void refreshProgressBars() { + mainBar.setProgress(expenseRegister.getTotalSum()/generalBudget.getMaxAmount()); + + foodBar.setProgress((generalBudget.getBudgetItem(ExpenseCategory.FOOD).getBudgetAmount() - overview.getBudgetItemMinusExpense(ExpenseCategory.FOOD))/generalBudget.getBudgetItem(ExpenseCategory.FOOD).getBudgetAmount()); + clothesBar.setProgress((generalBudget.getBudgetItem(ExpenseCategory.CLOTHES).getBudgetAmount() - overview.getBudgetItemMinusExpense(ExpenseCategory.CLOTHES))/generalBudget.getBudgetItem(ExpenseCategory.CLOTHES).getBudgetAmount()); + bookBar.setProgress((generalBudget.getBudgetItem(ExpenseCategory.BOOKS).getBudgetAmount() - overview.getBudgetItemMinusExpense(ExpenseCategory.BOOKS))/generalBudget.getBudgetItem(ExpenseCategory.BOOKS).getBudgetAmount()); + otherBar.setProgress((generalBudget.getBudgetItem(ExpenseCategory.OTHER).getBudgetAmount() - overview.getBudgetItemMinusExpense(ExpenseCategory.OTHER))/generalBudget.getBudgetItem(ExpenseCategory.OTHER).getBudgetAmount()); } + /** + * 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 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 { + //Instantiate expense register + 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; + } + + /** * Method for disabling the date picker, yet having its opacity at max. */ @@ -147,6 +231,7 @@ public class MainMenu { date.setStyle("-fx-opacity: 1"); date.getEditor().setStyle("-fx-opacity: 1"); } + @FXML private void switchScene(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(); diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java index dae0ae45d1f277854e182ac9934fcb0e664d2887..8a95589edbe458e647af77461866141986bd2889 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java @@ -33,7 +33,7 @@ public class SceneController { } public void switchIncome(ActionEvent event) throws IOException { - FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/IncomeAndExpenses.fxml")); + FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/MainMenuNew.fxml")); Parent root = loader.load(); stage = (Stage)((Node)event.getSource()).getScene().getWindow(); scene = new Scene(root); diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java index 6a4e46adbe1b2ba8f282341931c8dae900e6c59c..3a51dc780dc47f3c9c7458a5b050aaa638895a40 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java @@ -1,10 +1,12 @@ package no.ntnu.idatt1002.demo.data.Budget; +import java.util.Arrays; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; import java.sql.Date; import java.time.Duration; import java.time.LocalDate; +import java.time.Month; import java.time.ZoneId; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; @@ -110,22 +112,16 @@ public class GeneralBudget { } /** - * This method returns the amount of days left in the budget period as a string. + * This method returns the amount of days left in the budget period as a long. * * @return the amount of days left in the budget period * */ public long getDaysLeftOfBudgetPeriod() { - LocalDate today = LocalDate.now(); - LocalDate end = today.plus(Duration.ofDays(getBudgetPeriod())); + Month thisMonth = LocalDate.now().getMonth(); + int dayOfTheMonth = LocalDate.now().getDayOfMonth(); - Date todaysDate = (Date) Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant()); - Date endDate = (Date) Date.from(end.atStartOfDay(ZoneId.systemDefault()).toInstant()); - - Calendar cStart = Calendar.getInstance(); cStart.setTime(todaysDate); - Calendar cEnd = Calendar.getInstance(); cEnd.setTime(endDate); - - return ChronoUnit.DAYS.between((Temporal) cStart, (Temporal) cEnd); + return thisMonth.maxLength() - dayOfTheMonth; } /** @@ -176,6 +172,27 @@ public class GeneralBudget { return budgetItems.stream().anyMatch(budgetItem -> budgetItem.getBudgetCategory().equals(category)); } + /** + * This method checks if there are unused categories in the budget. + + * @return True, if the list contains unused categories. Else returns false. + */ + private boolean hasUnusedCategories() { + return budgetItems.size() != ExpenseCategory.values().length; + } + + /** + * Method that adds the remaining, unused categories to the budget list. + * Useful, as unused categories can be automatically added without user input. + */ + public void addUnusedCategories() { + if (hasUnusedCategories()) { + Arrays.stream(ExpenseCategory.values()). + filter(category -> !hasBudgetCategory(category)). + forEach(category -> budgetItems.add(new BudgetItem(0, "", category))); + } + } + /** * This method returns the totalSum of all budgetItems in the generalBudget * diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Overview.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Overview.java index b67373787d8931c2f7dce027fc0973343cf85f3b..4ed63a56b399c3daf710fcbab9ce2ab3c67bf989 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Overview.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Overview.java @@ -40,8 +40,8 @@ public class Overview { public Overview(IncomeRegister incomeRegister, ExpenseRegister expenseRegister, GeneralBudget budget){ this.incomeRegister = incomeRegister; this.expenseRegister = expenseRegister; - updateBalance(); this.budget = budget; + updateBalance(); } /** @@ -77,12 +77,12 @@ public class Overview { } /** - * Method for updating the total revenue - * by taking the total sum of the income register + * Method for updating the total balance + * by taking the max amount of the budget * minus the total sum of the expense register. */ public void updateBalance(){ - balance = incomeRegister.getTotalSum() - expenseRegister.getTotalSum(); + balance = budget.getMaxAmount() - expenseRegister.getTotalSum(); } /** diff --git a/src/main/resources/Budget/Budget.budget b/src/main/resources/Budget/Budget.budget index 08268ba2af084bf2db5f94d3fe18e34abc46302a..a1737fc130bfe60600356758782f254944a1a8bb 100644 --- a/src/main/resources/Budget/Budget.budget +++ b/src/main/resources/Budget/Budget.budget @@ -9,3 +9,11 @@ budgetAmount=100.0 budgetCategory=CLOTHES budgetDescription= +budgetAmount=0.0 +budgetCategory=BOOKS +budgetDescription= + +budgetAmount=0.0 +budgetCategory=OTHER +budgetDescription= + diff --git a/src/main/resources/Economics/Expense.register b/src/main/resources/Economics/Expense.register index 92307dbae98c7c3d8218aa817518484f044637c5..6435f9469d30071fc836f95e7337b9dd542af9a1 100644 --- a/src/main/resources/Economics/Expense.register +++ b/src/main/resources/Economics/Expense.register @@ -4,14 +4,8 @@ amount=12.0 isRecurring=Not recurring category=CLOTHES -date=2023-03-11 -description=1111 -amount=121.0 -isRecurring=Not recurring -category=OTHER - date=2023-03-26 -amount=10.0 +amount=200.0 isRecurring=Not recurring category=FOOD diff --git a/src/main/resources/Economics/Income.register b/src/main/resources/Economics/Income.register index 9283de81358e6b0251a23bc79202aabacb304fab..51f3e3d3818ae28ec3300f8405b47cf4b908714e 100644 --- a/src/main/resources/Economics/Income.register +++ b/src/main/resources/Economics/Income.register @@ -1,27 +1,11 @@ +date=2023-03-24 +description=studie +amount=900.0 +isRecurring=Recurring +category=STUDENT_LOAN + date=2023-03-25 amount=100.0 isRecurring=Not recurring category=GIFT -date=2023-03-31 -amount=500.0 -isRecurring=Not recurring -category=SALARY - -date=2023-04-15 -description=bday -amount=1000.0 -isRecurring=Not recurring -category=GIFT - -date=2023-04-15 -description=finn -amount=100.0 -isRecurring=Not recurring -category=SALARY - -date=2023-04-17 -amount=500.0 -isRecurring=Not recurring -category=STUDENT_LOAN - diff --git a/src/main/resources/view/BudgetNewest.fxml b/src/main/resources/view/BudgetNewest.fxml index 4ec1bfc83b5b596f08919ec4024d25cb84daccf6..4999c3a596dddcbf7794d029d43408e6b97985ce 100644 --- a/src/main/resources/view/BudgetNewest.fxml +++ b/src/main/resources/view/BudgetNewest.fxml @@ -60,7 +60,7 @@ </Pane> </left> <center> - <Label fx:id="title" text="Budget" textAlignment="CENTER" BorderPane.alignment="CENTER"> + <Label fx:id="title" text="BUDGET" textAlignment="CENTER" BorderPane.alignment="CENTER"> <font> <Font name="Lucida Console" size="48.0" /> </font> @@ -146,9 +146,18 @@ <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> </rowConstraints> <children> - <VBox prefHeight="475.0" prefWidth="485.0"> + <VBox prefHeight="475.0" prefWidth="523.0"> <children> - <TableView fx:id="budgetTableView" maxHeight="450.0" prefHeight="450.0" prefWidth="523.0"> + <Pane> + <children> + <Label alignment="CENTER" layoutX="135.0" text="Max amount: 1000"> + <font> + <Font name="Lucida Console" size="24.0" /> + </font> + </Label> + </children> + </Pane> + <TableView fx:id="budgetTableView" maxHeight="450.0" prefHeight="239.0" prefWidth="523.0"> <columns> <TableColumn fx:id="categoryCol" prefWidth="75.0" text="Category" /> <TableColumn fx:id="amountCol" prefWidth="75.0" text="Amount" /> @@ -164,17 +173,18 @@ </items> </ContextMenu> </contextMenu> + <VBox.margin> + <Insets top="10.0" /> + </VBox.margin> </TableView> - <Label text="Sum: "> - <font> - <Font name="Lucida Console" size="14.0" /> - </font> - </Label> </children> + <GridPane.margin> + <Insets top="30.0" /> + </GridPane.margin> </VBox> <Pane GridPane.columnIndex="1"> <children> - <PieChart fx:id="budgetPieChart" layoutX="39.0" layoutY="5.0" legendSide="RIGHT" maxHeight="444.0" maxWidth="512.0" prefHeight="444.0" prefWidth="446.0" /> + <PieChart fx:id="budgetPieChart" layoutX="105.0" layoutY="33.0" legendSide="RIGHT" maxHeight="444.0" maxWidth="512.0" prefHeight="302.0" prefWidth="354.0" /> </children> </Pane> </children> diff --git a/src/main/resources/view/IncomeAndExpenses.fxml b/src/main/resources/view/IncomeAndExpenses.fxml index 4482bd928b0240789467281eb9d7d5abcf98f8ea..ace8dac9bd98a83179a74ed731f6620441287a4e 100644 --- a/src/main/resources/view/IncomeAndExpenses.fxml +++ b/src/main/resources/view/IncomeAndExpenses.fxml @@ -63,11 +63,16 @@ </Pane> </left> <center> - <Label fx:id="title" text="INCOME AND EXPENSES" textAlignment="CENTER" BorderPane.alignment="CENTER"> - <font> - <Font name="Lucida Console" size="48.0" /> - </font> - </Label> + <Pane BorderPane.alignment="CENTER"> + <children> + <Label fx:id="title" layoutX="100.0" layoutY="47.0" text="INCOME AND EXPENSES" textAlignment="CENTER"> + <font> + <Font name="Lucida Console" size="48.0" /> + </font> + </Label> + <Label fx:id="errorMsg" layoutX="309.0" layoutY="112.0" opacity="0.0" text="Couldn't load saved data" textAlignment="CENTER" textFill="#f20808" /> + </children> + </Pane> </center> <VBox.margin> <Insets left="15.0" /> @@ -90,6 +95,7 @@ </ImageView> </graphic> </MenuButton> + <Button mnemonicParsing="false" text="popup explaining how to edit/delete" /> </children> <BorderPane.margin> <Insets left="30.0" /> @@ -155,11 +161,6 @@ </ContextMenu> </contextMenu> </TableView> - <Label text="Sum: "> - <font> - <Font name="Lucida Console" size="14.0" /> - </font> - </Label> </children> </VBox> <Pane GridPane.columnIndex="1" GridPane.rowIndex="1"> @@ -179,6 +180,11 @@ <Font name="Lucida Console" size="14.0" /> </font> </Text> + <Label fx:id="inSum" layoutX="64.0" layoutY="4.0" text="Sum: "> + <font> + <Font name="Lucida Console" size="14.0" /> + </font> + </Label> </children> </Pane> <VBox GridPane.rowIndex="3"> @@ -203,9 +209,9 @@ </ContextMenu> </contextMenu> </TableView> - <Label text="Sum: "> + <Label fx:id="expSum" text="Sum: "> <font> - <Font name="Lucida Console" size="14.0" /> + <Font name="Lucida Console" size="12.0" /> </font> </Label> </children> diff --git a/src/main/resources/view/dualList.fxml b/src/main/resources/view/dualList.fxml new file mode 100644 index 0000000000000000000000000000000000000000..4b464415c3a8294b45b369c53197c4b9d4b0fc6a --- /dev/null +++ b/src/main/resources/view/dualList.fxml @@ -0,0 +1,212 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.Cursor?> +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.ComboBox?> +<?import javafx.scene.control.MenuButton?> +<?import javafx.scene.control.MenuItem?> +<?import javafx.scene.control.TableColumn?> +<?import javafx.scene.control.TableView?> +<?import javafx.scene.image.Image?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.ColumnConstraints?> +<?import javafx.scene.layout.GridPane?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.Region?> +<?import javafx.scene.layout.RowConstraints?> +<?import javafx.scene.layout.StackPane?> +<?import javafx.scene.layout.VBox?> +<?import javafx.scene.shape.Rectangle?> +<?import javafx.scene.text.Font?> +<?import javafx.scene.text.Text?> + +<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="500.0" prefWidth="750.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.IncomeExpenseController"> + <children> + <ImageView fitHeight="500.0" fitWidth="750.0" pickOnBounds="true"> + <image> + <Image url="@../Images/backgroundMini.jpg" /> + </image> + <cursor> + <Cursor fx:constant="DEFAULT" /> + </cursor> + </ImageView> + <VBox layoutX="60.0" layoutY="77.0"> + <children> + <GridPane prefHeight="400.0" prefWidth="600.0"> + <columnConstraints> + <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="550.0" prefWidth="550.0" /> + <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="80.0" prefWidth="80.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" valignment="CENTER" vgrow="SOMETIMES" /> + </rowConstraints> + <children> + <HBox alignment="CENTER" prefHeight="300.0" prefWidth="600.0" spacing="30.0" GridPane.columnSpan="2" GridPane.rowIndex="1"> + <children> + <VBox> + <children> + <StackPane> + <children> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#f8f8f8" height="18.0" stroke="#d9cccc" strokeType="INSIDE" width="300.0" /> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Income" /> + </children> + </StackPane> + <StackPane> + <children> + <TableView fx:id="expenseTableView" minWidth="300.0" prefHeight="260.0" prefWidth="485.0"> + <columns> + <TableColumn fx:id="inDateCol" prefWidth="75.0" text="Date" /> + <TableColumn fx:id="inAmountCol" prefWidth="75.0" text="Amount" /> + <TableColumn fx:id="inCategoryCol" prefWidth="75.0" text="Category" /> + <TableColumn fx:id="inDescriptionCol" prefWidth="75.0" text="Description" /> + <TableColumn fx:id="inRecurringCol" prefWidth="75.0" text="Recurring" /> + </columns> + <columnResizePolicy> + <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> + </columnResizePolicy> + </TableView> + <Rectangle arcHeight="5.0" arcWidth="5.0" disable="true" fill="LIME" height="234.0" opacity="0.1" stroke="#d9cccc" strokeType="INSIDE" translateY="13.0" width="300.0" /> + </children> + </StackPane> + <StackPane> + <children> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#f8f8f8" height="18.0" stroke="#d9cccc" strokeType="INSIDE" width="300.0" StackPane.alignment="TOP_LEFT" /> + <HBox prefHeight="18.0" prefWidth="517.0"> + <children> + <Text fx:id="inSum" strokeType="OUTSIDE" strokeWidth="0.0" text="Sum: "> + <HBox.margin> + <Insets left="2.0" /> + </HBox.margin> + </Text> + <Region prefHeight="18.0" prefWidth="74.0" /> + <Text fx:id="sum" strokeType="OUTSIDE" strokeWidth="0.0" text="Text" /> + </children> + </HBox> + </children> + </StackPane> + </children> + </VBox> + <VBox> + <children> + <StackPane> + <children> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#f8f8f8" height="18.0" stroke="#d9cccc" strokeType="INSIDE" width="300.0" /> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Expenses" /> + </children> + </StackPane> + <StackPane> + <children> + <TableView fx:id="expenseTableView1" minWidth="300.0" prefHeight="260.0" prefWidth="485.0"> + <columns> + <TableColumn fx:id="expDateCol" prefWidth="75.0" text="Date" /> + <TableColumn fx:id="expAmountCol" prefWidth="75.0" text="Amount" /> + <TableColumn fx:id="expCategoryCol" prefWidth="75.0" text="Category" /> + <TableColumn fx:id="expDescriptionCol" prefWidth="75.0" text="Description" /> + <TableColumn fx:id="expRecurringCol" prefWidth="75.0" text="Recurring" /> + </columns> + <columnResizePolicy> + <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> + </columnResizePolicy> + </TableView> + <Rectangle arcHeight="5.0" arcWidth="5.0" disable="true" fill="RED" height="234.0" opacity="0.1" stroke="RED" strokeType="INSIDE" translateY="13.0" width="300.0" /> + </children> + </StackPane> + <StackPane> + <children> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#f8f8f8" height="18.0" stroke="#d9cccc" strokeType="INSIDE" width="300.0" StackPane.alignment="TOP_LEFT" /> + <HBox prefHeight="18.0" prefWidth="517.0"> + <children> + <Text fx:id="expSum" strokeType="OUTSIDE" strokeWidth="0.0" text="Sum: "> + <HBox.margin> + <Insets left="2.0" /> + </HBox.margin> + </Text> + <Region prefHeight="18.0" prefWidth="74.0" /> + <Text fx:id="sum1" strokeType="OUTSIDE" strokeWidth="0.0" text="Text" /> + </children> + </HBox> + </children> + </StackPane> + </children> + </VBox> + </children> + </HBox> + <HBox alignment="CENTER_LEFT" prefWidth="410.0" spacing="5.0"> + <children> + <MenuButton mnemonicParsing="false" text="MenuButton"> + <items> + <MenuItem fx:id="addIncome" mnemonicParsing="false" onAction="#handleAddBtn" text="Income" /> + <MenuItem fx:id="addExpense" mnemonicParsing="false" onAction="#handleAddBtn" text="Expense" /> + </items> + <graphic> + <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@../Images/add_image.png" /> + </image> + </ImageView> + </graphic> + </MenuButton> + </children> + <opaqueInsets> + <Insets /> + </opaqueInsets> + <padding> + <Insets bottom="5.0" /> + </padding> + </HBox> + <VBox alignment="CENTER_LEFT" prefHeight="200.0" prefWidth="100.0" spacing="5.0" GridPane.columnIndex="1"> + <children> + <ComboBox fx:id="filter" prefWidth="150.0" promptText="Show"> + <opaqueInsets> + <Insets /> + </opaqueInsets> + <VBox.margin> + <Insets bottom="5.0" /> + </VBox.margin> + </ComboBox> + </children> + </VBox> + <Button fx:id="returnBtn" mnemonicParsing="false" onAction="#returnToMainMenu" prefWidth="150.0" text="Continue" GridPane.columnIndex="1" GridPane.rowIndex="2"> + <GridPane.margin> + <Insets bottom="5.0" /> + </GridPane.margin> + </Button> + <StackPane alignment="CENTER_LEFT" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.valignment="CENTER"> + <children> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#f8f8f8" height="26.0" stroke="#d9cccc" strokeLineCap="ROUND" strokeType="INSIDE" width="550.0" StackPane.alignment="CENTER_LEFT"> + <StackPane.margin> + <Insets bottom="4.0" /> + </StackPane.margin> + </Rectangle> + <HBox alignment="CENTER_LEFT" minHeight="15.0" prefHeight="18.0" prefWidth="510.0"> + <children> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Total Disbosable Amount: "> + <HBox.margin> + <Insets left="3.0" /> + </HBox.margin> + </Text> + <Region prefHeight="18.0" prefWidth="100.0" /> + <Text fx:id="sum11" strokeType="OUTSIDE" strokeWidth="0.0" text="Text" /> + </children> + <StackPane.margin> + <Insets bottom="4.0" /> + </StackPane.margin> + </HBox> + </children> + </StackPane> + </children> + </GridPane> + </children> + </VBox> + <Button layoutX="14.0" layoutY="14.0" mnemonicParsing="false" text="Return" /> + <Text layoutX="197.0" layoutY="60.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Income/Expenses" textAlignment="CENTER"> + <font> + <Font size="48.0" /> + </font> + </Text> + </children> +</AnchorPane> diff --git a/src/main/resources/view/newBudgetBudgert.fxml b/src/main/resources/view/newBudgetBudgert.fxml new file mode 100644 index 0000000000000000000000000000000000000000..6ecdfb435715aeb506a3d9c4b9afb31d204557a5 --- /dev/null +++ b/src/main/resources/view/newBudgetBudgert.fxml @@ -0,0 +1,230 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.Cursor?> +<?import javafx.scene.chart.PieChart?> +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.ComboBox?> +<?import javafx.scene.control.ContextMenu?> +<?import javafx.scene.control.DatePicker?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.control.MenuItem?> +<?import javafx.scene.control.TableColumn?> +<?import javafx.scene.control.TableView?> +<?import javafx.scene.image.Image?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.ColumnConstraints?> +<?import javafx.scene.layout.GridPane?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.RowConstraints?> +<?import javafx.scene.layout.StackPane?> +<?import javafx.scene.layout.VBox?> +<?import javafx.scene.shape.Rectangle?> +<?import javafx.scene.text.Font?> +<?import javafx.scene.text.Text?> + +<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="500.0" prefWidth="750.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.BudgetController"> + <children> + <ImageView fitHeight="500.0" fitWidth="750.0" pickOnBounds="true"> + <image> + <Image url="@../Images/backgroundMini.jpg" /> + </image> + <cursor> + <Cursor fx:constant="DEFAULT" /> + </cursor> + </ImageView> + <VBox> + <children> + <GridPane prefHeight="479.0" prefWidth="690.0"> + <VBox.margin> + <Insets left="30.0" right="30.0" /> + </VBox.margin> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints maxHeight="281.36448669433594" minHeight="10.0" prefHeight="90.96714782714844" vgrow="SOMETIMES" /> + <RowConstraints maxHeight="424.19301986694336" minHeight="10.0" prefHeight="388.03285217285156" vgrow="SOMETIMES" /> + </rowConstraints> + <children> + <VBox GridPane.rowIndex="1"> + <children> + <GridPane prefHeight="473.0" prefWidth="1055.0"> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" maxWidth="600.0" minWidth="10.0" prefWidth="120.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints maxHeight="443.18585205078125" minHeight="10.0" prefHeight="53.365501403808594" vgrow="SOMETIMES" /> + <RowConstraints maxHeight="443.18585205078125" minHeight="10.0" prefHeight="325.1344985961914" vgrow="SOMETIMES" /> + <RowConstraints maxHeight="230.78009033203125" minHeight="10.0" prefHeight="48.083740234375" vgrow="SOMETIMES" /> + </rowConstraints> + <children> + <HBox alignment="CENTER" spacing="10.0" GridPane.columnIndex="1" GridPane.rowIndex="1"> + <children> + <PieChart fx:id="budgetPieChart" legendSide="RIGHT" maxHeight="444.0" maxWidth="512.0" prefHeight="302.0" prefWidth="354.0" /> + </children> + </HBox> + <StackPane alignment="CENTER_LEFT" prefWidth="375.0" GridPane.rowIndex="2" GridPane.rowSpan="2147483647"> + <children> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#f8f8f8" height="26.0" stroke="#d9cccc" strokeLineCap="ROUND" strokeType="INSIDE" width="350.0" /> + <HBox alignment="CENTER_LEFT" minHeight="15.0" prefHeight="36.0" prefWidth="345.0"> + <children> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Overview: (used of disposal)"> + <HBox.margin> + <Insets left="3.0" /> + </HBox.margin> + <font> + <Font size="14.0" /> + </font> + </Text> + </children> + </HBox> + </children> + </StackPane> + <HBox alignment="CENTER_RIGHT" GridPane.columnIndex="1" GridPane.rowIndex="2"> + <children> + <Button fx:id="returnBtn1" mnemonicParsing="false" onAction="#returnToMainMenu" prefWidth="100.0" text="Back"> + <font> + <Font size="14.0" /> + </font> + </Button> + <Button fx:id="returnBtn2" mnemonicParsing="false" onAction="#returnToMainMenu" prefWidth="100.0" text="Continue"> + <font> + <Font size="14.0" /> + </font> + </Button> + </children> + </HBox> + <GridPane> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" vgrow="SOMETIMES" /> + </rowConstraints> + <children> + <HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0" spacing="10.0"> + <children> + <Button fx:id="addBtn" mnemonicParsing="false" onAction="#handleAddBtn" prefHeight="25.0" prefWidth="60.0"> + <font> + <Font name="Lucida Console" size="12.0" /> + </font> + <graphic> + <ImageView fitHeight="20.0" fitWidth="40.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@../Images/add.png" /> + </image> + </ImageView> + </graphic> + </Button> + <Button fx:id="editBtn" mnemonicParsing="false" onAction="#handleEditBtn" prefHeight="25.0" prefWidth="60.0"> + <font> + <Font name="Lucida Console" size="12.0" /> + </font> + <graphic> + <ImageView fitHeight="20.0" fitWidth="40.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@../Images/edit.png" /> + </image> + </ImageView> + </graphic> + </Button> + <Button fx:id="deleteBtn" mnemonicParsing="false" onAction="#handleDeleteBtn" prefHeight="25.0" prefWidth="60.0"> + <font> + <Font name="Lucida Console" size="12.0" /> + </font> + <graphic> + <ImageView fitHeight="20.0" fitWidth="40.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@../Images/delete.png" /> + </image> + </ImageView> + </graphic> + </Button> + </children> + </HBox> + <HBox alignment="CENTER_LEFT" GridPane.columnIndex="1"> + <children> + <ComboBox fx:id="filter" prefHeight="20.0" prefWidth="180.0" promptText="Show"> + <opaqueInsets> + <Insets /> + </opaqueInsets> + </ComboBox> + </children> + </HBox> + </children> + </GridPane> + <HBox alignment="CENTER" GridPane.columnIndex="1"> + <children> + <Label alignment="CENTER" prefHeight="20.0" prefWidth="533.0" text="Amount at disposal: 1000"> + <font> + <Font size="24.0" /> + </font> + </Label> + </children> + </HBox> + <TableView fx:id="budgetTableView" maxHeight="450.0" prefHeight="305.0" prefWidth="340.0" GridPane.rowIndex="1"> + <columns> + <TableColumn fx:id="categoryCol" prefWidth="75.0" text="Category" /> + <TableColumn fx:id="amountCol" prefWidth="75.0" text="Amount" /> + <TableColumn fx:id="descriptionCol" prefWidth="75.0" text="Description" /> + </columns> + <columnResizePolicy> + <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> + </columnResizePolicy> + <contextMenu> + <ContextMenu> + <items> + <MenuItem mnemonicParsing="false" text="Unspecified Action" /> + </items> + </ContextMenu> + </contextMenu> + </TableView> + </children> + <padding> + <Insets left="10.0" /> + </padding> + </GridPane> + </children> + </VBox> + <GridPane> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" vgrow="SOMETIMES" /> + </rowConstraints> + <children> + <Button fx:id="returnBtn" alignment="CENTER" mnemonicParsing="false" onAction="#returnToMainMenu" text="Return to Main Menu"> + <font> + <Font size="14.0" /> + </font> + </Button> + <Label fx:id="title" text="Budget Setup" textAlignment="CENTER" GridPane.columnIndex="1"> + <font> + <Font size="36.0" /> + </font> + </Label> + <StackPane GridPane.columnIndex="2"> + <children> + <DatePicker fx:id="date" prefWidth="175.0" /> + <Label fx:id="daysLeftLbl" text="Days left: 31"> + <font> + <Font name="Lucida Console" size="14.0" /> + </font> + </Label> + </children> + </StackPane> + </children> + </GridPane> + </children> + </GridPane> + </children> + </VBox> + </children> +</AnchorPane> diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudgetTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudgetTest.java index d4ee06433f68d8e3b85eae037f72237ecb3c70b2..e13fadab307e970a357391f1c93a3b2b317e1518 100644 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudgetTest.java +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudgetTest.java @@ -1,7 +1,9 @@ package no.ntnu.idatt1002.demo.data.Budget; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -104,4 +106,37 @@ class GeneralBudgetTest { assertTrue(list.isEmpty()); } + + @Nested + class UnusedCategories { + GeneralBudget budget; + List<BudgetItem> list; + + @BeforeEach + void setUp() { + list = new ArrayList<>(); + budget = new GeneralBudget(12, list, 1200); + budget.addToBudget(10, "Books", ExpenseCategory.BOOKS); + } + + @Test + @DisplayName("Test addUnusedCategories adds unused categories") + void hasUnusedCategories_with_full_budget() { + assertEquals(1, budget.getBudgetItems().size()); + budget.addUnusedCategories(); + + assertEquals(4, budget.getBudgetItems().size()); + } + + } + + + /* @Test + @DisplayName("Gets the number of days left in the month. 17 has to be changed to the actual number of days left in the month.") + void get_days_left_of_the_month(){ + List<BudgetItem> list = new ArrayList<>(); + GeneralBudget budget1 = new GeneralBudget(12, list, 1200); + + assertEquals(17, budget1.getDaysLeftOfBudgetPeriod()); + }*/ } \ No newline at end of file