diff --git a/src/de/deadlocker8/budgetmaster/logic/MonthInOutSum.java b/src/de/deadlocker8/budgetmaster/logic/MonthInOutSum.java index df77968c8310b3c1484f4aa62f081f16049101c4..edc3d0f572bd1d87283f6d5dbcb0d56581e53fca 100644 --- a/src/de/deadlocker8/budgetmaster/logic/MonthInOutSum.java +++ b/src/de/deadlocker8/budgetmaster/logic/MonthInOutSum.java @@ -1,27 +1,22 @@ package de.deadlocker8.budgetmaster.logic; +import java.util.ArrayList; + import org.joda.time.DateTime; public class MonthInOutSum { private int month; private int year; - private int budgetIN; - private int budgetOUT; + private ArrayList<CategoryInOutSum> sums; - public MonthInOutSum(int month, int year, int budgetIN, int budgetOUT) + public MonthInOutSum(int month, int year, ArrayList<CategoryInOutSum> sums) { this.month = month; this.year = year; - this.budgetIN = budgetIN; - this.budgetOUT = budgetOUT; - } - - public DateTime getDate() - { - return DateTime.now().withYear(year).withMonthOfYear(month).withDayOfMonth(1); + this.sums = sums; } - + public int getMonth() { return month; @@ -32,29 +27,41 @@ public class MonthInOutSum return year; } - public int getBudgetIN() + public ArrayList<CategoryInOutSum> getSums() { - return budgetIN; + return sums; } - - public void setBudgetIN(int budgetIN) + + public DateTime getDate() { - this.budgetIN = budgetIN; + return DateTime.now().withYear(year).withMonthOfYear(month).withDayOfMonth(1); } - - public int getBudgetOUT() + + public int getBudgetIN() { - return budgetOUT; + int budget = 0; + for(CategoryInOutSum currentCategorySum : sums) + { + budget += currentCategorySum.getBudgetIN(); + } + + return budget; } - - public void setBudgetOUT(int budgetOUT) + + public int getBudgetOUT() { - this.budgetOUT = budgetOUT; + int budget = 0; + for(CategoryInOutSum currentCategorySum : sums) + { + budget += currentCategorySum.getBudgetOUT(); + } + + return budget; } @Override public String toString() { - return "MonthInOutSum [month=" + month + ", year=" + year + ", budgetIN=" + budgetIN + ", budgetOUT=" + budgetOUT + "]"; + return "MonthInOutSum [month=" + month + ", year=" + year + ", sums=" + sums + "]"; } } \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/logic/chartGenerators/BarChartGenerator.java b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/BarChartGenerator.java index 22238d36676e749fead31964ffb4f0e51e3d567d..c3276887a3f8d70f7770ac4acf9f0b54c1e0fcb3 100644 --- a/src/de/deadlocker8/budgetmaster/logic/chartGenerators/BarChartGenerator.java +++ b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/BarChartGenerator.java @@ -17,6 +17,7 @@ import javafx.scene.control.Label; import javafx.scene.control.Tooltip; import javafx.scene.input.MouseEvent; +@Deprecated public class BarChartGenerator { private ArrayList<MonthInOutSum> monthInOutSums; diff --git a/src/de/deadlocker8/budgetmaster/logic/chartGenerators/CategoriesChartGenerator.java b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/CategoriesChartGenerator.java index 6a476534d02e913172c195b8b00272d09c0dddf0..70c380cdd257b9892694009c2f1d265eb76213a6 100644 --- a/src/de/deadlocker8/budgetmaster/logic/chartGenerators/CategoriesChartGenerator.java +++ b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/CategoriesChartGenerator.java @@ -29,17 +29,17 @@ public class CategoriesChartGenerator this.useBudgetIN = useBudgetIN; this.currency = currency; this.total = getTotal(categoryInOutSums, useBudgetIN); - } + } public VBox generate() { - VBox chartWithLegend = new VBox(); + VBox generatedChart = new VBox(); HBox chart = new HBox(); chart.setMinHeight(30); Label labelTitle = new Label(title); labelTitle.setStyle("-fx-font-size: 16; -fx-font-weight: bold;"); - chartWithLegend.getChildren().add(labelTitle); + generatedChart.getChildren().add(labelTitle); VBox.setMargin(labelTitle, new Insets(0, 0, 10, 0)); for(CategoryInOutSum currentItem : categoryInOutSums) @@ -68,9 +68,9 @@ public class CategoriesChartGenerator currentPart.setTooltip(tooltip); } - chartWithLegend.getChildren().add(chart); + generatedChart.getChildren().add(chart); - return chartWithLegend; + return generatedChart; } public GridPane generateLegend() diff --git a/src/de/deadlocker8/budgetmaster/logic/chartGenerators/LineChartGenerator.java b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/LineChartGenerator.java index cabefdf42b85d86ace0627b839133e4d565f3c51..8888a5bd140f83f962677215f344645f5326de5f 100644 --- a/src/de/deadlocker8/budgetmaster/logic/chartGenerators/LineChartGenerator.java +++ b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/LineChartGenerator.java @@ -17,7 +17,6 @@ import javafx.scene.control.Label; import javafx.scene.control.Tooltip; import javafx.scene.input.MouseEvent; -//TODO ? public class LineChartGenerator { private ArrayList<MonthInOutSum> monthInOutSums; @@ -49,7 +48,7 @@ public class LineChartGenerator String label = currentItem.getDate().toString("MMMM YY"); seriesIN.getData().add(new XYChart.Data<String, Number>(label, currentItem.getBudgetIN() / 100.0)); - seriesOUT.getData().add(new XYChart.Data<String, Number>(label, currentItem.getBudgetOUT() / 100.0)); + seriesOUT.getData().add(new XYChart.Data<String, Number>(label, -currentItem.getBudgetOUT() / 100.0)); } generatedChart.getData().add(seriesIN); @@ -135,8 +134,6 @@ public class LineChartGenerator } } - // TODO color income green and payments red - return generatedChart; } } \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/logic/chartGenerators/MonthChartGenerator.java b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/MonthChartGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..3b5140da494b0ad1fae519f54e6452b4a70ae759 --- /dev/null +++ b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/MonthChartGenerator.java @@ -0,0 +1,199 @@ +package de.deadlocker8.budgetmaster.logic.chartGenerators; + +import java.util.ArrayList; + +import de.deadlocker8.budgetmaster.logic.CategoryInOutSum; +import de.deadlocker8.budgetmaster.logic.Helpers; +import de.deadlocker8.budgetmaster.logic.MonthInOutSum; +import javafx.geometry.Insets; +import javafx.geometry.Orientation; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.control.Separator; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.text.TextAlignment; +import tools.ConvertTo; + +public class MonthChartGenerator +{ + private ArrayList<MonthInOutSum> monthInOutSums; + private String currency; + + public MonthChartGenerator(ArrayList<MonthInOutSum> monthInOutSums, String currency) + { + this.monthInOutSums = monthInOutSums; + this.currency = currency; + } + + public HBox generate() + { + HBox generatedChart = new HBox(); + generatedChart.setAlignment(Pos.TOP_CENTER); + generatedChart.setSpacing(25); + + double total = getMaximum(monthInOutSums); + + for(MonthInOutSum currentMonthSum : monthInOutSums) + { + VBox chartPart = new VBox(); + chartPart.setAlignment(Pos.TOP_CENTER); + + HBox hboxChart = new HBox(); + hboxChart.setAlignment(Pos.BOTTOM_CENTER); + hboxChart.setSpacing(10); + VBox chartIncome = generateChart(currentMonthSum.getSums(), total, true); + hboxChart.getChildren().add(chartIncome); + HBox.setHgrow(chartIncome, Priority.ALWAYS); + VBox chartPayment = generateChart(currentMonthSum.getSums(), total, false); + hboxChart.getChildren().add(chartPayment); + HBox.setHgrow(chartPayment, Priority.ALWAYS); + + chartPart.getChildren().add(hboxChart); + VBox.setVgrow(hboxChart, Priority.ALWAYS); + + Label labelTitle = new Label(currentMonthSum.getDate().toString("MMMM \nYY")); + labelTitle.setStyle("-fx-font-size: 12;"); + labelTitle.setTextAlignment(TextAlignment.CENTER); + chartPart.getChildren().add(labelTitle); + VBox.setMargin(labelTitle, new Insets(10, 0, 0, 0)); + + generatedChart.getChildren().add(chartPart); + generatedChart.getChildren().add(new Separator(Orientation.VERTICAL)); + } + + return generatedChart; + } + + private VBox generateChart(ArrayList<CategoryInOutSum> categoryInOutSums, double total, boolean useBudgetIN) + { + VBox result = new VBox(); + Label labelAmount = new Label(Helpers.NUMBER_FORMAT.format(getTotal(categoryInOutSums, useBudgetIN)).replace(".", ",") + currency); + labelAmount.setStyle("-fx-font-size: 12; -fx-font-weight: bold;"); + result.getChildren().add(labelAmount); + VBox.setMargin(labelAmount, new Insets(0, 0, 10, 0)); + + VBox chart = new VBox(); + chart.setAlignment(Pos.BOTTOM_CENTER); + + for(CategoryInOutSum currentItem : categoryInOutSums) + { + Label currentPart = new Label(); + currentPart.setStyle("-fx-background-color: " + ConvertTo.toRGBHexWithoutOpacity(currentItem.getColor())); + currentPart.prefWidthProperty().bind(chart.widthProperty()); + chart.getChildren().add(currentPart); + + double value; + if(useBudgetIN) + { + value = currentItem.getBudgetIN() / 100.0; + } + else + { + value = -currentItem.getBudgetOUT() / 100.0; + } + + double percentage = value / total; + + currentPart.setMinHeight(0); + currentPart.prefHeightProperty().bind(chart.heightProperty().multiply(percentage)); + + Tooltip tooltip = new Tooltip(); + tooltip.setText(currentItem.getName() + "\n"+ Helpers.NUMBER_FORMAT.format(percentage * 100) + " %\n" + Helpers.NUMBER_FORMAT.format(value).replace(".", ",") + currency);// + currentPart.setTooltip(tooltip); + } + + result.getChildren().add(chart); + VBox.setVgrow(chart, Priority.ALWAYS); + + return result; + } + + public GridPane generateLegend() + { + ArrayList<HBox> legendItems = new ArrayList<>(); + for(CategoryInOutSum currentItem : monthInOutSums.get(0).getSums()) + { + String label = currentItem.getName(); + if(label.equals("NONE")) + { + label = "Keine Kategorie"; + } + legendItems.add(getLegendItem(label, currentItem.getColor())); + } + + int legendWidth = (int)Math.ceil(Math.sqrt(legendItems.size())); + GridPane legend = new GridPane(); + legend.setPadding(new Insets(10)); + legend.setHgap(20); + legend.setVgap(10); + legend.setAlignment(Pos.CENTER); + legend.setStyle("-fx-background-color: #EEEEEE; -fx-border-color: #212121; -fx-border-width: 1; -fx-border-radius: 5;"); + + for(int i = 0; i < legendItems.size(); i++) + { + int columnIndex = i % legendWidth; + int rowIndex = i / 4; + legend.add(legendItems.get(i), columnIndex, rowIndex); + } + + return legend; + } + + private HBox getLegendItem(String name, Color color) + { + HBox legendItem = new HBox(); + Label labelCircle = new Label(); + labelCircle.setMinWidth(20); + labelCircle.setMinHeight(20); + labelCircle.setStyle("-fx-background-color: " + ConvertTo.toRGBHexWithoutOpacity(color) + "; -fx-background-radius: 50%; -fx-border-width: 1; -fx-border-color: black - fx-border-radius: 50%"); + + Label labelText = new Label(name); + labelText.setStyle("-fx-font-weight: bold;"); + + legendItem.getChildren().add(labelCircle); + legendItem.getChildren().add(labelText); + HBox.setMargin(labelText, new Insets(0, 0, 0, 5)); + + return legendItem; + } + + private double getTotal(ArrayList<CategoryInOutSum> categoryInOutSums, boolean useBudgetIN) + { + double total = 0; + for(CategoryInOutSum currentItem : categoryInOutSums) + { + if(useBudgetIN) + { + total += currentItem.getBudgetIN() / 100.0; + } + else + { + total += -currentItem.getBudgetOUT() / 100.0; + } + } + return total; + } + + private double getMaximum(ArrayList<MonthInOutSum> monthInOutSums) + { + double maximum = 0; + for(MonthInOutSum currentItem : monthInOutSums) + { + if(currentItem.getBudgetIN() > maximum) + { + maximum = currentItem.getBudgetIN(); + } + + if(Math.abs(currentItem.getBudgetOUT()) > maximum) + { + maximum = Math.abs(currentItem.getBudgetOUT()); + } + } + return maximum / 100.0; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/ui/ChartController.java b/src/de/deadlocker8/budgetmaster/ui/ChartController.java index bd77351fb4ec4ae4dc4a717e7fb681bd45b0c631..b4ceb4f63bcf6469f8a5692b83a906b0b26ab55c 100644 --- a/src/de/deadlocker8/budgetmaster/ui/ChartController.java +++ b/src/de/deadlocker8/budgetmaster/ui/ChartController.java @@ -10,14 +10,14 @@ import de.deadlocker8.budgetmaster.logic.CategoryInOutSum; import de.deadlocker8.budgetmaster.logic.Helpers; import de.deadlocker8.budgetmaster.logic.MonthInOutSum; import de.deadlocker8.budgetmaster.logic.ServerConnection; -import de.deadlocker8.budgetmaster.logic.chartGenerators.BarChartGenerator; import de.deadlocker8.budgetmaster.logic.chartGenerators.CategoriesChartGenerator; import de.deadlocker8.budgetmaster.logic.chartGenerators.LineChartGenerator; +import de.deadlocker8.budgetmaster.logic.chartGenerators.MonthChartGenerator; import fontAwesome.FontIcon; import fontAwesome.FontIconType; import javafx.collections.FXCollections; import javafx.fxml.FXML; -import javafx.scene.chart.BarChart; +import javafx.geometry.Insets; import javafx.scene.chart.LineChart; import javafx.scene.control.Accordion; import javafx.scene.control.Alert.AlertType; @@ -26,8 +26,12 @@ import javafx.scene.control.ComboBox; import javafx.scene.control.DateCell; import javafx.scene.control.DatePicker; import javafx.scene.control.RadioButton; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.ScrollPane.ScrollBarPolicy; import javafx.scene.control.ToggleGroup; import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; @@ -43,7 +47,7 @@ public class ChartController implements Refreshable @FXML private DatePicker datePickerStart; @FXML private VBox vboxChartCategories; @FXML private DatePicker datePickerEnd; - @FXML private AnchorPane anchorPaneChartMonth; + @FXML private VBox vboxChartMonth; @FXML private Button buttonChartCategoriesShow; @FXML private ComboBox<String> comboBoxStartMonth; @FXML private ComboBox<String> comboBoxStartYear; @@ -62,13 +66,13 @@ public class ChartController implements Refreshable anchorPaneMain.setStyle("-fx-background-color: #F4F4F4;"); vboxChartCategories.setStyle("-fx-background-color: #F4F4F4;"); vboxChartCategories.setSpacing(20); - anchorPaneChartMonth.setStyle("-fx-background-color: #F4F4F4;"); + vboxChartMonth.setStyle("-fx-background-color: #F4F4F4;"); FontIcon iconShow = new FontIcon(FontIconType.CHECK); iconShow.setSize(16); iconShow.setColor(Color.WHITE); buttonChartCategoriesShow.setStyle("-fx-background-color: #2E79B9;"); buttonChartCategoriesShow.setGraphic(iconShow); - + FontIcon iconShow2 = new FontIcon(FontIconType.CHECK); iconShow2.setSize(16); iconShow2.setColor(Color.WHITE); @@ -114,6 +118,7 @@ public class ChartController implements Refreshable radioButtonLines.setToggleGroup(toggleGroup); accordion.setExpandedPane(accordion.getPanes().get(0)); + vboxChartMonth.setSpacing(15); } public void chartCategoriesShow() @@ -149,7 +154,7 @@ public class ChartController implements Refreshable public void chartMonthShow() { - anchorPaneChartMonth.getChildren().clear(); + vboxChartMonth.getChildren().clear(); String startMonth = comboBoxStartMonth.getValue(); String startYear = comboBoxStartYear.getValue(); @@ -169,51 +174,44 @@ public class ChartController implements Refreshable } try - { + { ServerConnection connection = new ServerConnection(controller.getSettings()); ArrayList<MonthInOutSum> sums = connection.getMonthInOutSum(startDate, endDate); + + vboxChartMonth.getChildren().clear(); if(radioButtonBars.isSelected()) { - chartMonthBarShow(sums); + ScrollPane scrollPane = new ScrollPane(); + scrollPane.setVbarPolicy(ScrollBarPolicy.NEVER); + scrollPane.setFocusTraversable(false); + scrollPane.setStyle("-fx-background-color: transparent; -fx-background-insets: 0; -fx-border-color: transparent; -fx-border-width: 0; -fx-border-insets: 0;"); + scrollPane.setPadding(new Insets(0, 0, 10, 0)); + + MonthChartGenerator generator = new MonthChartGenerator(sums, controller.getSettings().getCurrency()); + HBox generatedChart = generator.generate(); + scrollPane.setContent(generatedChart); + generatedChart.prefHeightProperty().bind(scrollPane.heightProperty().subtract(30)); + vboxChartMonth.getChildren().add(scrollPane); + VBox.setVgrow(scrollPane, Priority.ALWAYS); + vboxChartMonth.getChildren().add(generator.generateLegend()); } else - { - chartMonthLineShow(sums); - } + { + LineChartGenerator generator = new LineChartGenerator(sums, controller.getSettings().getCurrency()); + LineChart<String, Number> chartMonth = generator.generate(); + vboxChartMonth.getChildren().add(chartMonth); + VBox.setVgrow(chartMonth, Priority.ALWAYS); + } } catch(Exception e) { Logger.error(e); - //TODO - //controller.showConnectionErrorAlert(e.getMessage()); + // TODO + // controller.showConnectionErrorAlert(e.getMessage()); } } - public void chartMonthBarShow(ArrayList<MonthInOutSum> sums) - { - - BarChartGenerator generator = new BarChartGenerator(sums, controller.getSettings().getCurrency()); - BarChart<String, Number> chartMonth = generator.generate(); - anchorPaneChartMonth.getChildren().add(chartMonth); - AnchorPane.setTopAnchor(chartMonth, 0.0); - AnchorPane.setRightAnchor(chartMonth, 0.0); - AnchorPane.setBottomAnchor(chartMonth, 0.0); - AnchorPane.setLeftAnchor(chartMonth, 0.0); - } - - public void chartMonthLineShow(ArrayList<MonthInOutSum> sums) - { - anchorPaneChartMonth.getChildren().clear(); - LineChartGenerator generator = new LineChartGenerator(sums, controller.getSettings().getCurrency()); - LineChart<String, Number> chartMonth = generator.generate(); - anchorPaneChartMonth.getChildren().add(chartMonth); - AnchorPane.setTopAnchor(chartMonth, 0.0); - AnchorPane.setRightAnchor(chartMonth, 0.0); - AnchorPane.setBottomAnchor(chartMonth, 0.0); - AnchorPane.setLeftAnchor(chartMonth, 0.0); - } - @Override public void refresh() { diff --git a/src/de/deadlocker8/budgetmaster/ui/ChartTab.fxml b/src/de/deadlocker8/budgetmaster/ui/ChartTab.fxml index c8ae6a3bfdf7f903b4e583843536b14aa0ae4dba..7ffa60128c56a3f29617e506560b0eef6f6963ad 100644 --- a/src/de/deadlocker8/budgetmaster/ui/ChartTab.fxml +++ b/src/de/deadlocker8/budgetmaster/ui/ChartTab.fxml @@ -130,7 +130,7 @@ </HBox> </children> </HBox> - <AnchorPane fx:id="anchorPaneChartMonth" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS" /> + <VBox fx:id="vboxChartMonth" prefHeight="200.0" prefWidth="100.0" VBox.vgrow="ALWAYS" /> </children> </VBox> </content> diff --git a/src/de/deadlocker8/budgetmasterserver/server/charts/MonthInOutSum.java b/src/de/deadlocker8/budgetmasterserver/server/charts/MonthInOutSum.java index 1fe7eaa2612d00c1fe9d27912ea86f8a8f912fd9..cf770b2b9100989dc463e5f5e6a11a82cc2205e2 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/charts/MonthInOutSum.java +++ b/src/de/deadlocker8/budgetmasterserver/server/charts/MonthInOutSum.java @@ -8,6 +8,8 @@ import org.joda.time.DateTime; import com.google.gson.Gson; +import de.deadlocker8.budgetmaster.logic.Category; +import de.deadlocker8.budgetmaster.logic.CategoryInOutSum; import de.deadlocker8.budgetmaster.logic.Payment; import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; import spark.Request; @@ -44,24 +46,32 @@ public class MonthInOutSum implements Route { ArrayList<Payment> currentMonthPayments = new ArrayList<>(); currentMonthPayments.addAll(handler.getPayments(startDate.getYear(), startDate.getMonthOfYear())); - currentMonthPayments.addAll(handler.getRepeatingPayments(startDate.getYear(), startDate.getMonthOfYear())); + currentMonthPayments.addAll(handler.getRepeatingPayments(startDate.getYear(), startDate.getMonthOfYear())); - int sumIN = 0; - int sumOUT = 0; - - for(Payment currentPayment : currentMonthPayments) - { - if(currentPayment.getAmount() > 0) - { - sumIN += currentPayment.getAmount(); - } - else - { - sumOUT += -currentPayment.getAmount(); + ArrayList<CategoryInOutSum> sums = new ArrayList<>(); + + for(Category currentCategory : handler.getCategories()) + { + sums.add(new CategoryInOutSum(currentCategory.getID(), currentCategory.getName(), currentCategory.getColor(), 0, 0)); + CategoryInOutSum currentInOutSum = sums.get(sums.size() - 1); + for(Payment currentPayment : currentMonthPayments) + { + if(currentCategory.getID() == currentPayment.getCategoryID()) + { + int amount = currentPayment.getAmount(); + if(amount > 0) + { + currentInOutSum.setBudgetIN(currentInOutSum.getBudgetIN() + amount); + } + else + { + currentInOutSum.setBudgetOUT(currentInOutSum.getBudgetOUT() + amount); + } + } } } - monthInOutSums.add(new de.deadlocker8.budgetmaster.logic.MonthInOutSum(startDate.getMonthOfYear(), startDate.getYear(), sumIN, sumOUT)); + monthInOutSums.add(new de.deadlocker8.budgetmaster.logic.MonthInOutSum(startDate.getMonthOfYear(), startDate.getYear(), sums)); startDate = startDate.plusMonths(1); }