diff --git a/src/de/deadlocker8/budgetmaster/logic/MonthInOutSum.java b/src/de/deadlocker8/budgetmaster/logic/MonthInOutSum.java new file mode 100644 index 0000000000000000000000000000000000000000..df77968c8310b3c1484f4aa62f081f16049101c4 --- /dev/null +++ b/src/de/deadlocker8/budgetmaster/logic/MonthInOutSum.java @@ -0,0 +1,60 @@ +package de.deadlocker8.budgetmaster.logic; + +import org.joda.time.DateTime; + +public class MonthInOutSum +{ + private int month; + private int year; + private int budgetIN; + private int budgetOUT; + + public MonthInOutSum(int month, int year, int budgetIN, int budgetOUT) + { + this.month = month; + this.year = year; + this.budgetIN = budgetIN; + this.budgetOUT = budgetOUT; + } + + public DateTime getDate() + { + return DateTime.now().withYear(year).withMonthOfYear(month).withDayOfMonth(1); + } + + public int getMonth() + { + return month; + } + + public int getYear() + { + return year; + } + + public int getBudgetIN() + { + return budgetIN; + } + + public void setBudgetIN(int budgetIN) + { + this.budgetIN = budgetIN; + } + + public int getBudgetOUT() + { + return budgetOUT; + } + + public void setBudgetOUT(int budgetOUT) + { + this.budgetOUT = budgetOUT; + } + + @Override + public String toString() + { + return "MonthInOutSum [month=" + month + ", year=" + year + ", budgetIN=" + budgetIN + ", budgetOUT=" + budgetOUT + "]"; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/logic/ServerConnection.java b/src/de/deadlocker8/budgetmaster/logic/ServerConnection.java index 5b807553a63066b1749f96c5416022108df11d1e..64e7cb4202ac6d126c10eed8967182e47bdb9809 100644 --- a/src/de/deadlocker8/budgetmaster/logic/ServerConnection.java +++ b/src/de/deadlocker8/budgetmaster/logic/ServerConnection.java @@ -348,4 +348,28 @@ public class ServerConnection return null; } } + + public ArrayList<MonthInOutSum> getMonthInOutSum(DateTime startDate, DateTime endDate) throws Exception + { + URL url = new URL(settings.getUrl() + "/charts/monthInOutSum?secret=" + Helpers.getURLEncodedString(settings.getSecret()) + + "&startDate=" + startDate.toString("yyyy-MM-dd") + + "&endDate=" + endDate.toString("yyyy-MM-dd")); + HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); + httpsCon.setDoOutput(true); + httpsCon.setRequestMethod("GET"); + + if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) + { + String result = Read.getStringFromInputStream(httpsCon.getInputStream()); + // required by GSON + Type listType = new TypeToken<ArrayList<MonthInOutSum>>() + { + }.getType(); + return gson.fromJson(result, listType); + } + else + { + return null; + } + } } \ 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 new file mode 100644 index 0000000000000000000000000000000000000000..c8918cc82836d30eed69a9cdc5ccbf1359127f26 --- /dev/null +++ b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/BarChartGenerator.java @@ -0,0 +1,88 @@ +package de.deadlocker8.budgetmaster.logic.chartGenerators; + +import java.util.ArrayList; + +import de.deadlocker8.budgetmaster.logic.Helpers; +import de.deadlocker8.budgetmaster.logic.MonthInOutSum; +import javafx.event.EventHandler; +import javafx.geometry.Point2D; +import javafx.scene.Node; +import javafx.scene.chart.BarChart; +import javafx.scene.chart.CategoryAxis; +import javafx.scene.chart.NumberAxis; +import javafx.scene.chart.XYChart; +import javafx.scene.control.Tooltip; +import javafx.scene.input.MouseEvent; + +public class BarChartGenerator +{ + private ArrayList<MonthInOutSum> monthInOutSums; + private String currency; + + public BarChartGenerator(ArrayList<MonthInOutSum> monthInOutSums, String currency) + { + this.monthInOutSums = monthInOutSums; + this.currency = currency; + } + + public BarChart<String, Number> generate() + { + final CategoryAxis xAxis = new CategoryAxis(); + final NumberAxis yAxis = new NumberAxis(); + final BarChart<String, Number> generatedChart = new BarChart<>(xAxis, yAxis); + generatedChart.setTitle(null); + + xAxis.setLabel(""); + yAxis.setLabel("Summe in " + currency); + + XYChart.Series<String, Number> seriesIN = new XYChart.Series<String, Number>(); + seriesIN.setName("Einnahmen"); + XYChart.Series<String, Number> seriesOUT = new XYChart.Series<String, Number>(); + seriesOUT.setName("Ausgaben"); + + for(MonthInOutSum currentItem : monthInOutSums) + { + String label = currentItem.getDate().toString("MMMM"); + + 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)); + } + + generatedChart.getData().add(seriesIN); + generatedChart.getData().add(seriesOUT); + + generatedChart.setLegendVisible(true); + + // add tooltip to every segment + generatedChart.getData().stream().forEach(tool -> { + for(XYChart.Data<String, Number> data : tool.getData()) + { + Tooltip tooltip = new Tooltip(); + + tooltip.setText(Helpers.NUMBER_FORMAT.format(data.getYValue()).replace(".", ",") + currency); + Tooltip.install(tool.getNode(), tooltip); + Node node = data.getNode(); + node.setOnMouseEntered(new EventHandler<MouseEvent>() + { + @Override + public void handle(MouseEvent event) + { + Point2D p = node.localToScreen(event.getX() + 5, event.getY() + 7); + tooltip.show(node, p.getX(), p.getY()); + } + }); + node.setOnMouseExited(new EventHandler<MouseEvent>() + { + + @Override + public void handle(MouseEvent event) + { + tooltip.hide(); + } + }); + } + }); + + return generatedChart; + } +} \ 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 7947993a720ebbd41343a3657b6f2f94ccba61b3..6a4113d9a73f512895b3e57bb314137c0aa9bb43 100644 --- a/src/de/deadlocker8/budgetmaster/ui/ChartController.java +++ b/src/de/deadlocker8/budgetmaster/ui/ChartController.java @@ -6,11 +6,14 @@ import java.util.ArrayList; import org.joda.time.DateTime; import de.deadlocker8.budgetmaster.logic.CategoryInOutSum; +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.PieChartGenerator; import fontAwesome.FontIcon; import fontAwesome.FontIconType; import javafx.fxml.FXML; +import javafx.scene.chart.BarChart; import javafx.scene.control.Accordion; import javafx.scene.control.Button; import javafx.scene.control.DateCell; @@ -84,8 +87,35 @@ public class ChartController implements Refreshable hboxChartCategories.getChildren().add(generator.generate()); generator = new PieChartGenerator("Ausgaben nach Kategorien", sums, false, controller.getSettings().getCurrency()); hboxChartCategories.getChildren().add(generator.generate()); + } + catch(Exception e) + { + Logger.error(e); + //TODO + //controller.showConnectionErrorAlert(e.getMessage()); + } + } + + public void chartMonthShow() + { + //DEBUG get date from comboboxes + DateTime startDate = controller.getCurrentDate().withMonthOfYear(1); + DateTime endDate = controller.getCurrentDate().withMonthOfYear(12); + + try + { + ServerConnection connection = new ServerConnection(controller.getSettings()); + //DEBUG + ArrayList<MonthInOutSum> sums = connection.getMonthInOutSum(startDate, endDate); - accordion.setExpandedPane(accordion.getPanes().get(0)); + anchorPaneChartMonth.getChildren().clear(); + 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); } catch(Exception e) { @@ -106,5 +136,10 @@ public class ChartController implements Refreshable datePickerEnd.setValue(endDate); chartCategoriesShow(); + + //chart month + chartMonthShow(); + + accordion.setExpandedPane(accordion.getPanes().get(0)); } } \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/ui/ChartTab.fxml b/src/de/deadlocker8/budgetmaster/ui/ChartTab.fxml index b92658ac87161e1a3727e6214c658fc221b91469..41854a7626374a68c1127802d6b37187657de848 100644 --- a/src/de/deadlocker8/budgetmaster/ui/ChartTab.fxml +++ b/src/de/deadlocker8/budgetmaster/ui/ChartTab.fxml @@ -3,6 +3,7 @@ <?import javafx.geometry.Insets?> <?import javafx.scene.control.Accordion?> <?import javafx.scene.control.Button?> +<?import javafx.scene.control.ComboBox?> <?import javafx.scene.control.DatePicker?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.TitledPane?> @@ -66,12 +67,56 @@ </font> </TitledPane> <TitledPane animated="false" text="Einnahmen/Ausgaben pro Monat"> - <content> - <AnchorPane fx:id="anchorPaneChartMonth" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" /> - </content> <font> <Font name="System Bold" size="12.0" /> </font> + <content> + <VBox spacing="20.0"> + <children> + <HBox alignment="CENTER" prefHeight="8.0" prefWidth="750.0"> + <children> + <HBox alignment="CENTER_RIGHT" spacing="10.0" HBox.hgrow="ALWAYS"> + <children> + <Label text="Von:"> + <font> + <Font name="System Bold" size="16.0" /> + </font> + </Label> + <ComboBox prefHeight="25.0" prefWidth="100.0" /> + <ComboBox prefWidth="70.0" /> + </children> + <HBox.margin> + <Insets right="15.0" /> + </HBox.margin> + </HBox> + <HBox alignment="CENTER_LEFT" spacing="10.0" HBox.hgrow="ALWAYS"> + <children> + <Label text="Bis:"> + <font> + <Font name="System Bold" size="16.0" /> + </font> + </Label> + <ComboBox prefWidth="100.0" /> + <ComboBox prefWidth="70.0" /> + <Button fx:id="buttonChartCategoriesShow1" mnemonicParsing="false" onAction="#chartCategoriesShow"> + <font> + <Font name="System Bold" size="12.0" /> + </font> + <HBox.margin> + <Insets left="15.0" /> + </HBox.margin> + </Button> + </children> + <HBox.margin> + <Insets left="15.0" /> + </HBox.margin> + </HBox> + </children> + </HBox> + <AnchorPane fx:id="anchorPaneChartMonth" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS" /> + </children> + </VBox> + </content> </TitledPane> </panes> </Accordion> diff --git a/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java b/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java index 67e52c46adc5ee17be3028f74ef3367e15c4e1d9..b5b450a50ace9761233c0be7da82218dddff9117 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java +++ b/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java @@ -26,6 +26,7 @@ import de.deadlocker8.budgetmasterserver.server.category.CategoryGetAll; import de.deadlocker8.budgetmasterserver.server.category.CategoryUpdate; import de.deadlocker8.budgetmasterserver.server.categorybudget.CategoryBudgetGet; import de.deadlocker8.budgetmasterserver.server.charts.CategoryInOutSumForMonth; +import de.deadlocker8.budgetmasterserver.server.charts.MonthInOutSum; import de.deadlocker8.budgetmasterserver.server.payment.normal.PaymentAdd; import de.deadlocker8.budgetmasterserver.server.payment.normal.PaymentDelete; import de.deadlocker8.budgetmasterserver.server.payment.normal.PaymentGet; @@ -114,6 +115,7 @@ public class SparkServer //charts get("/charts/categoryInOutSum", new CategoryInOutSumForMonth(handler, gson)); + get("/charts/monthInOutSum", new MonthInOutSum(handler, gson)); after((request, response) -> { diff --git a/src/de/deadlocker8/budgetmasterserver/server/charts/MonthInOutSum.java b/src/de/deadlocker8/budgetmasterserver/server/charts/MonthInOutSum.java new file mode 100644 index 0000000000000000000000000000000000000000..1fe7eaa2612d00c1fe9d27912ea86f8a8f912fd9 --- /dev/null +++ b/src/de/deadlocker8/budgetmasterserver/server/charts/MonthInOutSum.java @@ -0,0 +1,78 @@ +package de.deadlocker8.budgetmasterserver.server.charts; + +import static spark.Spark.halt; + +import java.util.ArrayList; + +import org.joda.time.DateTime; + +import com.google.gson.Gson; + +import de.deadlocker8.budgetmaster.logic.Payment; +import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import spark.Request; +import spark.Response; +import spark.Route; + +public class MonthInOutSum implements Route +{ + private DatabaseHandler handler; + private Gson gson; + + public MonthInOutSum(DatabaseHandler handler, Gson gson) + { + this.handler = handler; + this.gson = gson; + } + + @Override + public Object handle(Request req, Response res) throws Exception + { + if(!req.queryParams().contains("startDate") || !req.queryParams().contains("endDate")) + { + halt(400, "Bad Request"); + } + + try + { + DateTime startDate = DateTime.parse(req.queryMap("startDate").value()).withDayOfMonth(1); + DateTime endDate = DateTime.parse(req.queryMap("endDate").value()).withDayOfMonth(1); + + ArrayList<de.deadlocker8.budgetmaster.logic.MonthInOutSum> monthInOutSums = new ArrayList<>(); + + while(startDate.isBefore(endDate) || startDate.isEqual(endDate)) + { + ArrayList<Payment> currentMonthPayments = new ArrayList<>(); + currentMonthPayments.addAll(handler.getPayments(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(); + } + } + + monthInOutSums.add(new de.deadlocker8.budgetmaster.logic.MonthInOutSum(startDate.getMonthOfYear(), startDate.getYear(), sumIN, sumOUT)); + + startDate = startDate.plusMonths(1); + } + + return gson.toJson(monthInOutSums); + } + catch(IllegalStateException ex) + { + halt(500, "Internal Server Error"); + } + + return null; + } +} \ No newline at end of file