diff --git a/build/BudgetMaster.exe b/build/BudgetMaster.exe index adad21672f117432ecba5acc5880cd4de2b94f58..1615506ef0305ac54886c178beab441470a73f55 100644 Binary files a/build/BudgetMaster.exe and b/build/BudgetMaster.exe differ diff --git a/build/BudgetMasterClient.jar b/build/BudgetMasterClient.jar index 3fd4c2f6cf2308eab6c361001b09d5bb34572977..9bfc74a6eabb8c5f49012f35e1ebb3efb152a04d 100644 Binary files a/build/BudgetMasterClient.jar and b/build/BudgetMasterClient.jar differ diff --git a/build/BudgetMasterServer.jar b/build/BudgetMasterServer.jar index 7f2b0defbbc1662462382008db0793389329842a..19f220c3abad7970c1e5e5da95e622ac6799259b 100644 Binary files a/build/BudgetMasterServer.jar and b/build/BudgetMasterServer.jar differ diff --git a/src/de/deadlocker8/budgetmaster/logic/CategoryInOutSum.java b/src/de/deadlocker8/budgetmaster/logic/CategoryInOutSum.java new file mode 100644 index 0000000000000000000000000000000000000000..4b49c5b10d088b5b6a51ddf7c14a03d82e918c40 --- /dev/null +++ b/src/de/deadlocker8/budgetmaster/logic/CategoryInOutSum.java @@ -0,0 +1,82 @@ +package de.deadlocker8.budgetmaster.logic; + +import javafx.scene.paint.Color; + +public class CategoryInOutSum +{ + private int ID; + private String name; + private Color color; + private int budgetIN; + private int budgetOUT; + + public CategoryInOutSum(int ID, String name, Color color, int budgetIN, int budgetOUT) + { + this.ID = ID; + this.name = name; + this.color = color; + this.budgetIN = budgetIN; + this.budgetOUT = budgetOUT; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public Color getColor() + { + return color; + } + + public void setColor(Color color) + { + this.color = color; + } + + 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 boolean equals(Object obj) + { + if(this == obj) + return true; + if(obj == null) + return false; + if(getClass() != obj.getClass()) + return false; + CategoryInOutSum other = (CategoryInOutSum)obj; + if(ID != other.ID) + return false; + return true; + } + + @Override + public String toString() + { + return "CategoryInOutSum [ID=" + ID + ", name=" + name + ", color=" + color + ", budgetIN=" + budgetIN + ", budgetOUT=" + budgetOUT + "]"; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/logic/ExceptionHandler.java b/src/de/deadlocker8/budgetmaster/logic/ExceptionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..40aa9bbc3f3b3087a356a9f710e16c893d4fd6a3 --- /dev/null +++ b/src/de/deadlocker8/budgetmaster/logic/ExceptionHandler.java @@ -0,0 +1,40 @@ +package de.deadlocker8.budgetmaster.logic; + +public class ExceptionHandler +{ + public static String getMessageForException(Exception e) + { + if(e instanceof ServerConnectionException) + { + return handleServerConnectionException(e); + } + + if(e.getMessage() == null) + { + return "Unbekannter Fehler (" + e.getClass() + ")"; + } + + if(e.getMessage().contains("Connection refused")) + { + return "Server nicht erreichbar."; + } + else if(e.getMessage().contains("HTTPS hostname wrong")) + { + return "Der Server verwendet ein selbst signiertes Zertifkat für die Verschlüsselung. " + + "Aus Sicherheitsgründen werden diese Zertifikate standardmäßig blockiert. " + + "Wenn du dem Zertifikat trotzdem vertrauen möchtest, dann füge den Hostnamen des Servers zur Liste der vertrauenswürdigen Hosts in den Einstellungen hinzu."; + } + return e.getMessage(); + } + + private static String handleServerConnectionException(Exception e) + { + switch(e.getMessage()) + { + case "400": return "Der Server erhielt eine fehlerhafte Anfrage oder ungültige Parameter."; + case "401": return "Ungültiges Passwort."; + case "500": return "Beim Ausführen der Anfrage ist ein interner Serverfehler ist aufgetreten."; + default: return e.getMessage(); + } + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/logic/Helpers.java b/src/de/deadlocker8/budgetmaster/logic/Helpers.java index a3761e796fd6ad0bd0a435ffe141d0d397e9fa34..9190df3577db8a5f28558da7a167bc9e24489ed6 100644 --- a/src/de/deadlocker8/budgetmaster/logic/Helpers.java +++ b/src/de/deadlocker8/budgetmaster/logic/Helpers.java @@ -5,11 +5,16 @@ import java.net.URLEncoder; import java.text.DecimalFormat; import java.time.LocalDate; import java.time.format.DateTimeFormatter; +import java.util.ArrayList; public class Helpers { public static final DecimalFormat NUMBER_FORMAT = new DecimalFormat("0.00"); + public static final String COLOR_INCOME = "#22BAD9"; + public static final String COLOR_PAYMENT = "#F2612D"; + public static final String SALT = "ny9/Y+G|WrJ,82|oIYQQ X %i-sq#4,uA-qKPtwFPnw+s(k2`rV)^-a1|t{D3Z>S"; + public static String getURLEncodedString(String input) { try @@ -21,7 +26,7 @@ public class Helpers return input; } } - + public static String getDateString(LocalDate date) { if(date == null) @@ -31,4 +36,32 @@ public class Helpers DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); return date.format(formatter); } + + public static ArrayList<String> getMonthList() + { + ArrayList<String> monthNames = new ArrayList<>(); + monthNames.add("Januar"); + monthNames.add("Februar"); + monthNames.add("März"); + monthNames.add("April"); + monthNames.add("Mai"); + monthNames.add("Juni"); + monthNames.add("Juli"); + monthNames.add("August"); + monthNames.add("September"); + monthNames.add("Oktober"); + monthNames.add("November"); + monthNames.add("Dezember"); + return monthNames; + } + + public static ArrayList<String> getYearList() + { + ArrayList<String> years = new ArrayList<>(); + for(int i = 2000; i < 2100; i++) + { + years.add(String.valueOf(i)); + } + return years; + } } \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/logic/MonthInOutSum.java b/src/de/deadlocker8/budgetmaster/logic/MonthInOutSum.java new file mode 100644 index 0000000000000000000000000000000000000000..edc3d0f572bd1d87283f6d5dbcb0d56581e53fca --- /dev/null +++ b/src/de/deadlocker8/budgetmaster/logic/MonthInOutSum.java @@ -0,0 +1,67 @@ +package de.deadlocker8.budgetmaster.logic; + +import java.util.ArrayList; + +import org.joda.time.DateTime; + +public class MonthInOutSum +{ + private int month; + private int year; + private ArrayList<CategoryInOutSum> sums; + + public MonthInOutSum(int month, int year, ArrayList<CategoryInOutSum> sums) + { + this.month = month; + this.year = year; + this.sums = sums; + } + + public int getMonth() + { + return month; + } + + public int getYear() + { + return year; + } + + public ArrayList<CategoryInOutSum> getSums() + { + return sums; + } + + public DateTime getDate() + { + return DateTime.now().withYear(year).withMonthOfYear(month).withDayOfMonth(1); + } + + public int getBudgetIN() + { + int budget = 0; + for(CategoryInOutSum currentCategorySum : sums) + { + budget += currentCategorySum.getBudgetIN(); + } + + return budget; + } + + public int getBudgetOUT() + { + int budget = 0; + for(CategoryInOutSum currentCategorySum : sums) + { + budget += currentCategorySum.getBudgetOUT(); + } + + return budget; + } + + @Override + public String toString() + { + return "MonthInOutSum [month=" + month + ", year=" + year + ", sums=" + sums + "]"; + } +} \ 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 b6a04d740c68c6fb6e90fed593d980613061aa22..8443d3fe84ee30432e7aa18f0aef4b840e1e7cd6 100644 --- a/src/de/deadlocker8/budgetmaster/logic/ServerConnection.java +++ b/src/de/deadlocker8/budgetmaster/logic/ServerConnection.java @@ -3,6 +3,7 @@ package de.deadlocker8.budgetmaster.logic; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.PrintWriter; import java.lang.reflect.Type; import java.net.URL; import java.security.cert.X509Certificate; @@ -13,9 +14,12 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import org.joda.time.DateTime; + import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; +import de.deadlocker8.budgetmasterserver.logic.Database; import tools.ConvertTo; import tools.Read; @@ -49,7 +53,9 @@ public class ServerConnection SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); - HttpsURLConnection.setDefaultHostnameVerifier((hostname, sslSession) -> hostname.equals("localhost")); + + // check whitelist + HttpsURLConnection.setDefaultHostnameVerifier((hostname, sslSession) -> settings.getTrustedHosts().contains(hostname)); } /* @@ -73,10 +79,10 @@ public class ServerConnection } else { - return null; + return new ArrayList<>(); } } - + public Category getCategory(int ID) throws Exception { URL url = new URL(settings.getUrl() + "/category/single?secret=" + Helpers.getURLEncodedString(settings.getSecret()) + "&id=" + ID); @@ -101,9 +107,16 @@ public class ServerConnection HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); httpsCon.setRequestMethod("POST"); httpsCon.setDoInput(true); - InputStream stream = httpsCon.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); - reader.close(); + if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) + { + InputStream stream = httpsCon.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + reader.close(); + } + else + { + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); + } } public void updateCategory(Category category) throws Exception @@ -112,9 +125,16 @@ public class ServerConnection HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); httpsCon.setRequestMethod("PUT"); httpsCon.setDoInput(true); - InputStream stream = httpsCon.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); - reader.close(); + if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) + { + InputStream stream = httpsCon.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + reader.close(); + } + else + { + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); + } } public void deleteCategory(int ID) throws Exception @@ -123,11 +143,18 @@ public class ServerConnection HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); httpsCon.setRequestMethod("DELETE"); httpsCon.setDoInput(true); - InputStream stream = httpsCon.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); - reader.close(); + if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) + { + InputStream stream = httpsCon.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + reader.close(); + } + else + { + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); + } } - + /* * Payment */ @@ -137,7 +164,7 @@ public class ServerConnection HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); httpsCon.setDoOutput(true); httpsCon.setRequestMethod("GET"); - + if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) { String result = Read.getStringFromInputStream(httpsCon.getInputStream()); @@ -149,7 +176,7 @@ public class ServerConnection } else { - return null; + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); } } @@ -171,10 +198,10 @@ public class ServerConnection } else { - return null; + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); } } - + public RepeatingPayment getRepeatingPayment(int ID) throws Exception { URL url = new URL(settings.getUrl() + "/repeatingpayment/single?secret=" + Helpers.getURLEncodedString(settings.getSecret()) + "&id=" + ID); @@ -184,46 +211,51 @@ public class ServerConnection if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) { - String result = Read.getStringFromInputStream(httpsCon.getInputStream()); + String result = Read.getStringFromInputStream(httpsCon.getInputStream()); return gson.fromJson(result, RepeatingPayment.class); } else { - return null; + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); } } public void addNormalPayment(NormalPayment payment) throws Exception { - URL url = new URL(settings.getUrl() + "/payment?secret=" + Helpers.getURLEncodedString(settings.getSecret()) + - "&amount=" + payment.getAmount() + - "&date=" + payment.getDate() + - "&categoryID=" + payment.getCategoryID() + - "&name=" + Helpers.getURLEncodedString(payment.getName()) + - "&description=" + Helpers.getURLEncodedString(payment.getDescription())); + URL url = new URL(settings.getUrl() + "/payment?secret=" + Helpers.getURLEncodedString(settings.getSecret()) + "&amount=" + payment.getAmount() + "&date=" + payment.getDate() + "&categoryID=" + payment.getCategoryID() + "&name=" + Helpers.getURLEncodedString(payment.getName()) + + "&description=" + Helpers.getURLEncodedString(payment.getDescription())); HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); httpsCon.setRequestMethod("POST"); httpsCon.setDoInput(true); - InputStream stream = httpsCon.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); - reader.close(); + if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) + { + InputStream stream = httpsCon.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + reader.close(); + } + else + { + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); + } } public void updateNormalPayment(NormalPayment payment) throws Exception { - URL url = new URL(settings.getUrl() + "/payment?secret=" + settings.getSecret() + - "&id=" + payment.getID() + - "&amount=" + payment.getAmount() + - "&date=" + payment.getDate() + - "&categoryID=" + payment.getCategoryID() + - "&name=" + Helpers.getURLEncodedString(payment.getName()) + - "&description=" + Helpers.getURLEncodedString(payment.getDescription())); - HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); - httpsCon.setRequestMethod("PUT"); - httpsCon.setDoInput(true); - InputStream stream = httpsCon.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); - reader.close(); + URL url = new URL(settings.getUrl() + "/payment?secret=" + settings.getSecret() + "&id=" + payment.getID() + "&amount=" + payment.getAmount() + "&date=" + payment.getDate() + "&categoryID=" + payment.getCategoryID() + "&name=" + Helpers.getURLEncodedString(payment.getName()) + + "&description=" + Helpers.getURLEncodedString(payment.getDescription())); + HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); + httpsCon.setRequestMethod("PUT"); + httpsCon.setDoInput(true); + if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) + { + InputStream stream = httpsCon.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + reader.close(); + } + else + { + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); + } } public void addRepeatingPayment(RepeatingPayment payment) throws Exception @@ -235,21 +267,21 @@ public class ServerConnection repeatEndDate = "A"; } - URL url = new URL(settings.getUrl() + "/repeatingpayment?secret=" + Helpers.getURLEncodedString(settings.getSecret()) + - "&amount=" + payment.getAmount() + - "&date=" + payment.getDate() + - "&categoryID=" + payment.getCategoryID() + - "&name=" + Helpers.getURLEncodedString(payment.getName()) + - "&repeatInterval=" + payment.getRepeatInterval() + - "&repeatEndDate=" + repeatEndDate + - "&repeatMonthDay=" + payment.getRepeatMonthDay() + - "&description=" + Helpers.getURLEncodedString(payment.getDescription())); + URL url = new URL(settings.getUrl() + "/repeatingpayment?secret=" + Helpers.getURLEncodedString(settings.getSecret()) + "&amount=" + payment.getAmount() + "&date=" + payment.getDate() + "&categoryID=" + payment.getCategoryID() + "&name=" + Helpers.getURLEncodedString(payment.getName()) + + "&repeatInterval=" + payment.getRepeatInterval() + "&repeatEndDate=" + repeatEndDate + "&repeatMonthDay=" + payment.getRepeatMonthDay() + "&description=" + Helpers.getURLEncodedString(payment.getDescription())); HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); httpsCon.setRequestMethod("POST"); httpsCon.setDoInput(true); - InputStream stream = httpsCon.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); - reader.close(); + if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) + { + InputStream stream = httpsCon.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + reader.close(); + } + else + { + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); + } } public void deleteNormalPayment(NormalPayment payment) throws Exception @@ -258,22 +290,36 @@ public class ServerConnection HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); httpsCon.setRequestMethod("DELETE"); httpsCon.setDoInput(true); - InputStream stream = httpsCon.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); - reader.close(); + if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) + { + InputStream stream = httpsCon.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + reader.close(); + } + else + { + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); + } } - + public void deleteRepeatingPayment(RepeatingPaymentEntry payment) throws Exception { URL url = new URL(settings.getUrl() + "/repeatingpayment?secret=" + Helpers.getURLEncodedString(settings.getSecret()) + "&id=" + payment.getRepeatingPaymentID()); HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); httpsCon.setRequestMethod("DELETE"); httpsCon.setDoInput(true); - InputStream stream = httpsCon.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); - reader.close(); + if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) + { + InputStream stream = httpsCon.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + reader.close(); + } + else + { + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); + } } - + /* * CATEGORYBUDGET */ @@ -295,10 +341,10 @@ public class ServerConnection } else { - return null; + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); } } - + /* * REST */ @@ -311,12 +357,129 @@ public class ServerConnection if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) { - String result = Read.getStringFromInputStream(httpsCon.getInputStream()); + String result = Read.getStringFromInputStream(httpsCon.getInputStream()); return gson.fromJson(result, Integer.class); } else { - return 0; + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); + } + } + + /* + * DATABASE + */ + public void deleteDatabase() throws Exception + { + URL url = new URL(settings.getUrl() + "/database?secret=" + Helpers.getURLEncodedString(settings.getSecret())); + HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); + httpsCon.setRequestMethod("DELETE"); + httpsCon.setDoInput(true); + if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) + { + InputStream stream = httpsCon.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + reader.close(); + } + else + { + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); + } + } + + public String exportDatabase() throws Exception + { + URL url = new URL(settings.getUrl() + "/database?secret=" + Helpers.getURLEncodedString(settings.getSecret())); + HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); + httpsCon.setDoOutput(true); + httpsCon.setRequestMethod("GET"); + + if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) + { + return Read.getStringFromInputStream(httpsCon.getInputStream()); + } + else + { + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); + } + } + + public void importDatabase(Database database) throws Exception + { + String databaseJSON = new Gson().toJson(database); + + URL url = new URL(settings.getUrl() + "/database?secret=" + Helpers.getURLEncodedString(settings.getSecret())); + HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); + httpsCon.setRequestMethod("POST"); + httpsCon.setRequestProperty("Content-Type", "application/json"); + httpsCon.setRequestProperty("Accept", "application/json"); + httpsCon.setDoInput(true); + httpsCon.setDoOutput(true); + PrintWriter writer = new PrintWriter(httpsCon.getOutputStream()); + writer.write(databaseJSON); + writer.flush(); + writer.close(); + + if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) + { + InputStream stream = httpsCon.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + reader.close(); + } + else + { + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); + } + } + + /* + * CHARTS + */ + public ArrayList<CategoryInOutSum> getCategoryInOutSumForMonth(DateTime startDate, DateTime endDate) throws Exception + { + URL url = new URL(settings.getUrl() + "/charts/categoryInOutSum?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<CategoryInOutSum>>() + { + }.getType(); + return gson.fromJson(result, listType); + } + else + { + 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/ServerConnectionException.java b/src/de/deadlocker8/budgetmaster/logic/ServerConnectionException.java new file mode 100644 index 0000000000000000000000000000000000000000..c18a1663b3a6774129233997db6a08d99535f9e4 --- /dev/null +++ b/src/de/deadlocker8/budgetmaster/logic/ServerConnectionException.java @@ -0,0 +1,31 @@ +package de.deadlocker8.budgetmaster.logic; + +public class ServerConnectionException extends Exception +{ + private static final long serialVersionUID = 2784475774757068549L; + + public ServerConnectionException() + { + super(); + } + + public ServerConnectionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) + { + super(message, cause, enableSuppression, writableStackTrace); + } + + public ServerConnectionException(String message, Throwable cause) + { + super(message, cause); + } + + public ServerConnectionException(String message) + { + super(message); + } + + public ServerConnectionException(Throwable cause) + { + super(cause); + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/logic/Settings.java b/src/de/deadlocker8/budgetmaster/logic/Settings.java index 84a6b6ff524b6d19e94dcaf081e953e7ff75ab4e..f95ce96203ee507679c9ffb83ebc92efee79183a 100644 --- a/src/de/deadlocker8/budgetmaster/logic/Settings.java +++ b/src/de/deadlocker8/budgetmaster/logic/Settings.java @@ -1,11 +1,14 @@ package de.deadlocker8.budgetmaster.logic; +import java.util.ArrayList; + public class Settings { private String url; private String secret; private String currency; private boolean restActivated; + private ArrayList<String> trustedHosts; public Settings() { @@ -51,10 +54,20 @@ public class Settings { this.restActivated = restActivated; } + + public ArrayList<String> getTrustedHosts() + { + return trustedHosts; + } + + public void setTrustedHosts(ArrayList<String> trustedHosts) + { + this.trustedHosts = trustedHosts; + } @Override public String toString() { - return "Settings [url=" + url + ", secret=" + secret + ", currency=" + currency + ", restActivated=" + restActivated + "]"; + return "Settings [url=" + url + ", secret=" + secret + ", currency=" + currency + ", restActivated=" + restActivated + ", trustedHosts=" + trustedHosts + "]"; } } \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/logic/Utils.java b/src/de/deadlocker8/budgetmaster/logic/Utils.java index 55789dca5c6081740ca9e6a6fb2d427eda140c45..c9af40da56e4e6360256674d42ee8d139343969e 100644 --- a/src/de/deadlocker8/budgetmaster/logic/Utils.java +++ b/src/de/deadlocker8/budgetmaster/logic/Utils.java @@ -12,6 +12,7 @@ import java.util.ResourceBundle; import com.google.gson.Gson; +import de.deadlocker8.budgetmasterserver.logic.Database; import tools.PathUtils; public class Utils @@ -23,8 +24,7 @@ public class Utils Settings settings; try { - Gson gson = new Gson(); - PathUtils.checkFolder(new File(PathUtils.getOSindependentPath() + bundle.getString("folder"))); + Gson gson = new Gson(); Reader reader = Files.newBufferedReader(Paths.get(PathUtils.getOSindependentPath() + bundle.getString("folder") + "/settings.json"), Charset.forName("UTF-8")); settings = gson.fromJson(reader, Settings.class); reader.close(); @@ -40,9 +40,25 @@ public class Utils { Gson gson = new Gson(); String jsonString = gson.toJson(settings); - + PathUtils.checkFolder(new File(PathUtils.getOSindependentPath() + bundle.getString("folder"))); Writer writer = Files.newBufferedWriter(Paths.get(PathUtils.getOSindependentPath() + bundle.getString("folder") + "/settings.json"), Charset.forName("UTF-8")); writer.write(jsonString); writer.close(); } + + public static Database loadDatabaseJSON(File file) throws IOException + { + Gson gson = new Gson(); + Reader reader = Files.newBufferedReader(Paths.get(file.getAbsolutePath()), Charset.forName("UTF-8")); + Database database = gson.fromJson(reader, Database.class); + reader.close(); + return database; + } + + public static void saveDatabaseJSON(File file, String databaseJSON) throws IOException + { + Writer writer = Files.newBufferedWriter(Paths.get(file.getAbsolutePath()), Charset.forName("UTF-8")); + writer.write(databaseJSON); + writer.close(); + } } \ 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..c3276887a3f8d70f7770ac4acf9f0b54c1e0fcb3 --- /dev/null +++ b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/BarChartGenerator.java @@ -0,0 +1,129 @@ +package de.deadlocker8.budgetmaster.logic.chartGenerators; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; + +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.Label; +import javafx.scene.control.Tooltip; +import javafx.scene.input.MouseEvent; + +@Deprecated +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 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)); + } + + 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(); + } + }); + } + }); + + // style bar for incomes + for(Node n : generatedChart.lookupAll(".default-color0.chart-bar")) + { + n.setStyle("-fx-bar-fill: " + Helpers.COLOR_INCOME + ";"); + } + + // style bar for payments + for(Node n : generatedChart.lookupAll(".default-color1.chart-bar")) + { + n.setStyle("-fx-bar-fill: " + Helpers.COLOR_PAYMENT + ";"); + } + + //style legend item according to color + Set<Node> nodes = generatedChart.lookupAll(".chart-legend-item"); + if(nodes.size() > 0) + { + Iterator<Node> iterator = nodes.iterator(); + int counter = 0; + while(iterator.hasNext()) + { + Node node = iterator.next(); + if(node instanceof Label) + { + Label labelLegendItem = (Label)node; + if(counter == 0) + { + labelLegendItem.getGraphic().setStyle("-fx-background-color: " + Helpers.COLOR_INCOME + ";"); + } + else + { + labelLegendItem.getGraphic().setStyle("-fx-background-color: " + Helpers.COLOR_PAYMENT + ";"); + } + } + counter++; + } + } + + return generatedChart; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/logic/chartGenerators/CategoriesChartGenerator.java b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/CategoriesChartGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..77efcd715afaed985db53c936ea3e37041b3d6fc --- /dev/null +++ b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/CategoriesChartGenerator.java @@ -0,0 +1,154 @@ +package de.deadlocker8.budgetmaster.logic.chartGenerators; + +import java.util.ArrayList; + +import de.deadlocker8.budgetmaster.logic.CategoryInOutSum; +import de.deadlocker8.budgetmaster.logic.Helpers; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import tools.ConvertTo; + +public class CategoriesChartGenerator +{ + private String title; + private ArrayList<CategoryInOutSum> categoryInOutSums; + private boolean useBudgetIN; + private String currency; + private double total; + + public CategoriesChartGenerator(String title, ArrayList<CategoryInOutSum> categoryInOutSums, boolean useBudgetIN, String currency) + { + this.title = title; + if(categoryInOutSums == null) + { + this.categoryInOutSums = new ArrayList<>(); + } + else + { + this.categoryInOutSums = categoryInOutSums; + } + this.useBudgetIN = useBudgetIN; + this.currency = currency; + this.total = getTotal(this.categoryInOutSums, useBudgetIN); + } + + public VBox generate() + { + 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;"); + generatedChart.getChildren().add(labelTitle); + VBox.setMargin(labelTitle, new Insets(0, 0, 10, 0)); + + for(CategoryInOutSum currentItem : categoryInOutSums) + { + Label currentPart = new Label(); + currentPart.setStyle("-fx-background-color: " + ConvertTo.toRGBHexWithoutOpacity(currentItem.getColor())); + currentPart.prefHeightProperty().bind(chart.heightProperty()); + chart.getChildren().add(currentPart); + + double value; + if(useBudgetIN) + { + value = currentItem.getBudgetIN() / 100.0; + } + else + { + value = -currentItem.getBudgetOUT() / 100.0; + } + + double percentage = value / total; + + currentPart.prefWidthProperty().bind(chart.widthProperty().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); + } + + generatedChart.getChildren().add(chart); + + return generatedChart; + } + + public GridPane generateLegend() + { + 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;"); + + if(categoryInOutSums.size() == 0) + { + return legend; + } + + ArrayList<HBox> legendItems = new ArrayList<>(); + for(CategoryInOutSum currentItem : categoryInOutSums) + { + 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())); + + 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; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/logic/chartGenerators/LineChartGenerator.java b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/LineChartGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..8888a5bd140f83f962677215f344645f5326de5f --- /dev/null +++ b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/LineChartGenerator.java @@ -0,0 +1,139 @@ +package de.deadlocker8.budgetmaster.logic.chartGenerators; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; + +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.CategoryAxis; +import javafx.scene.chart.LineChart; +import javafx.scene.chart.NumberAxis; +import javafx.scene.chart.XYChart; +import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; +import javafx.scene.input.MouseEvent; + +public class LineChartGenerator +{ + private ArrayList<MonthInOutSum> monthInOutSums; + private String currency; + + public LineChartGenerator(ArrayList<MonthInOutSum> monthInOutSums, String currency) + { + this.monthInOutSums = monthInOutSums; + this.currency = currency; + } + + public LineChart<String, Number> generate() + { + final CategoryAxis xAxis = new CategoryAxis(); + final NumberAxis yAxis = new NumberAxis(); + final LineChart<String, Number> generatedChart = new LineChart<>(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 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)); + } + + 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(); + } + }); + } + }); + + // style line for incomes + for(Node n : generatedChart.lookupAll(".default-color0.chart-series-line")) + { + n.setStyle("-fx-stroke: " + Helpers.COLOR_INCOME + ";"); + } + + // style line dots for incomes + for(Node n : generatedChart.lookupAll(".default-color0.chart-line-symbol")) + { + n.setStyle("-fx-background-color: " + Helpers.COLOR_INCOME + ", white;"); + } + + // style line for payments + for(Node n : generatedChart.lookupAll(".default-color1.chart-series-line")) + { + n.setStyle("-fx-stroke: " + Helpers.COLOR_PAYMENT + ";"); + } + + // style line dots for payments + for(Node n : generatedChart.lookupAll(".default-color1.chart-line-symbol")) + { + n.setStyle("-fx-background-color: " + Helpers.COLOR_PAYMENT + ", white;"); + } + + // style legend item according to color + Set<Node> nodes = generatedChart.lookupAll(".chart-legend-item"); + if(nodes.size() > 0) + { + Iterator<Node> iterator = nodes.iterator(); + int counter = 0; + while(iterator.hasNext()) + { + Node node = iterator.next(); + if(node instanceof Label) + { + Label labelLegendItem = (Label)node; + if(counter == 0) + { + labelLegendItem.getGraphic().setStyle("-fx-background-color: " + Helpers.COLOR_INCOME + ";"); + } + else + { + labelLegendItem.getGraphic().setStyle("-fx-background-color: " + Helpers.COLOR_PAYMENT + ";"); + } + } + counter++; + } + } + + 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..35ae4150cb591b96c5bab30029a588107b5f55d3 --- /dev/null +++ b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/MonthChartGenerator.java @@ -0,0 +1,212 @@ +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) + { + if(monthInOutSums == null) + { + this.monthInOutSums = new ArrayList<>(); + } + else + { + 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() + { + 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;"); + + if(monthInOutSums.size() == 0) + { + return legend; + } + + 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())); + + 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/logic/chartGenerators/PieChartGenerator.java b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/PieChartGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..382d0a77dfede19bc254cfa0f3f40387c2af5c5f --- /dev/null +++ b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/PieChartGenerator.java @@ -0,0 +1,125 @@ +package de.deadlocker8.budgetmaster.logic.chartGenerators; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; + +import de.deadlocker8.budgetmaster.logic.CategoryInOutSum; +import de.deadlocker8.budgetmaster.logic.Helpers; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.EventHandler; +import javafx.geometry.Point2D; +import javafx.scene.Node; +import javafx.scene.chart.PieChart; +import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; +import javafx.scene.input.MouseEvent; +import tools.ConvertTo; + +@Deprecated +public class PieChartGenerator +{ + private String title; + private ArrayList<CategoryInOutSum> categoryInOutSums; + private boolean useBudgetIN; + private String currency; + + public PieChartGenerator(String title, ArrayList<CategoryInOutSum> categoryInOutSums, boolean useBudgetIN, String currency) + { + this.title = title; + this.categoryInOutSums = categoryInOutSums; + this.useBudgetIN = useBudgetIN; + this.currency = currency; + } + + public PieChart generate() + { + ArrayList<PieChart.Data> data = new ArrayList<>(); + + for(CategoryInOutSum currentItem : categoryInOutSums) + { + String label = currentItem.getName(); + if(label.equals("NONE")) + { + label = "Keine Kategorie"; + } + + if(useBudgetIN) + { + data.add(new PieChart.Data(label, currentItem.getBudgetIN()/100.0)); + } + else + { + data.add(new PieChart.Data(label, -currentItem.getBudgetOUT()/100.0)); + } + } + + ObservableList<PieChart.Data> pieChartData = FXCollections.observableArrayList(data); + + final PieChart chart = new PieChart(pieChartData); + chart.setTitle(title); + + //add tooltip to every segment that shows percentage as double value + chart.getData().stream().forEach(tool -> + { + Tooltip tooltip = new Tooltip(); + + double total = 0; + for(int i = 0; i < chart.getData().size(); i++) + { + PieChart.Data currentData = chart.getData().get(i); + total += currentData.getPieValue(); + String currentColor = ConvertTo.toRGBHexWithoutOpacity(categoryInOutSums.get(i).getColor()); + currentData.getNode().setStyle("-fx-pie-color: " + currentColor + ";"); + } + + //style legend item according to color + Set<Node> nodes = chart.lookupAll(".chart-legend-item"); + if(nodes.size() > 0) + { + Iterator<Node> iterator = nodes.iterator(); + int counter = 0; + while(iterator.hasNext()) + { + Node node = iterator.next(); + if(node instanceof Label) + { + Label labelLegendItem = (Label)node; + labelLegendItem.getGraphic().setStyle("-fx-background-color: " + ConvertTo.toRGBHexWithoutOpacity(categoryInOutSums.get(counter).getColor()) + ";"); + } + counter++; + } + } + + double pieValue = tool.getPieValue(); + double percentage = (pieValue / total) * 100; + String percent = String.valueOf(percentage); + percent = percent.substring(0, percent.indexOf(".") + 2); + + tooltip.setText(percent + " %\n" + Helpers.NUMBER_FORMAT.format(pieValue).replace(".", ",") + currency); + Tooltip.install(tool.getNode(), tooltip); + Node node = tool.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 chart; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/main/Main.java b/src/de/deadlocker8/budgetmaster/main/Main.java index de459fc7283f353dffd55a22b23e6ef6b476d50c..a2a24cf7e5355bfad397ae5afdc5b0ef628d1023 100644 --- a/src/de/deadlocker8/budgetmaster/main/Main.java +++ b/src/de/deadlocker8/budgetmaster/main/Main.java @@ -1,7 +1,6 @@ package de.deadlocker8.budgetmaster.main; import java.io.File; -import java.util.Arrays; import java.util.Locale; import java.util.ResourceBundle; @@ -12,14 +11,14 @@ import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.stage.Stage; -import logger.LogLevel; +import logger.FileOutputMode; import logger.Logger; import tools.PathUtils; public class Main extends Application { public static ResourceBundle bundle = ResourceBundle.getBundle("de/deadlocker8/budgetmaster/main/", Locale.GERMANY); - + @Override public void start(Stage stage) { @@ -28,13 +27,13 @@ public class Main extends Application FXMLLoader loader = new FXMLLoader(getClass().getClassLoader().getResource("de/deadlocker8/budgetmaster/ui/GUI.fxml")); Parent root = (Parent)loader.load(); - Scene scene = new Scene(root, 600, 650); + Scene scene = new Scene(root, 650, 650); ((Controller)loader.getController()).init(stage); stage.setResizable(true); - stage.setMinHeight(600); - stage.setMinWidth(600); + stage.setMinHeight(650); + stage.setMinWidth(610); stage.getIcons().add(new Image("/de/deadlocker8/budgetmaster/resources/icon.png")); stage.setTitle(bundle.getString("app.name")); stage.setScene(scene); @@ -47,25 +46,22 @@ public class Main extends Application } } - public static void main(String[] args) + @Override + public void init() throws Exception { - if(Arrays.asList(args).contains("debug")) - { - Logger.setLevel(LogLevel.ALL); - Logger.info("Running in Debug Mode"); - } - else - { - Logger.setLevel(LogLevel.ERROR); - } + Parameters params = getParameters(); + String logLevelParam = params.getNamed().get("loglevel"); + Logger.setLevel(logLevelParam); - PathUtils.checkFolder(new File(PathUtils.getOSindependentPath() + bundle.getString("folder"))); - File logFile = new File(PathUtils.getOSindependentPath() + bundle.getString("folder") + "/error.log"); - Logger.enableFileOutput(logFile); - + File logFolder = new File(PathUtils.getOSindependentPath() + bundle.getString("folder")); + PathUtils.checkFolder(logFolder); + Logger.enableFileOutput(logFolder, System.out, System.err, FileOutputMode.COMBINED); + Logger.appInfo(bundle.getString("app.name"), bundle.getString("version.name"), bundle.getString("version.code"), bundle.getString("version.date")); - + } + + public static void main(String[] args) + { launch(args); } - } \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/main/_de.properties b/src/de/deadlocker8/budgetmaster/main/_de.properties index 4e7b83ceadac1175fddd4a734c5008a4c550d4d2..888ab9545116e080a8d428638a00977aa0605ebd 100644 --- a/src/de/deadlocker8/budgetmaster/main/_de.properties +++ b/src/de/deadlocker8/budgetmaster/main/_de.properties @@ -1,7 +1,7 @@ app.name=BudgetMaster -version.code=2 -version.name=1.1.0 -version.date=12.04.17 +version.code=3 +version.name=1.2.0 +version.date=25.05.17 author=Robert Goldmann folder=Deadlocker/BudgetMaster diff --git a/src/de/deadlocker8/budgetmaster/ui/CategoryController.java b/src/de/deadlocker8/budgetmaster/ui/CategoryController.java index 0cdb686ec6f8cef2adb986699347810bbe9e468c..8a86686a8d5455a544882ddc0253e741c337d537 100644 --- a/src/de/deadlocker8/budgetmaster/ui/CategoryController.java +++ b/src/de/deadlocker8/budgetmaster/ui/CategoryController.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.util.ArrayList; import de.deadlocker8.budgetmaster.logic.Category; +import de.deadlocker8.budgetmaster.logic.ExceptionHandler; import de.deadlocker8.budgetmaster.logic.ServerConnection; import de.deadlocker8.budgetmaster.ui.cells.CategoryCell; import fontAwesome.FontIcon; @@ -161,8 +162,8 @@ public class CategoryController implements Refreshable } catch(Exception e) { - e.printStackTrace(); - controller.showConnectionErrorAlert(); + Logger.error(e); + controller.showConnectionErrorAlert(ExceptionHandler.getMessageForException(e)); } } diff --git a/src/de/deadlocker8/budgetmaster/ui/ChartController.java b/src/de/deadlocker8/budgetmaster/ui/ChartController.java index 30931aa6ffecb297d4b9702206f16b8a18de25e3..6f9e336ddcd7a7a27f4a9e367f14103874b7e9b8 100644 --- a/src/de/deadlocker8/budgetmaster/ui/ChartController.java +++ b/src/de/deadlocker8/budgetmaster/ui/ChartController.java @@ -1,11 +1,61 @@ package de.deadlocker8.budgetmaster.ui; +import java.time.LocalDate; +import java.util.ArrayList; + +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; + +import de.deadlocker8.budgetmaster.logic.CategoryInOutSum; +import de.deadlocker8.budgetmaster.logic.ExceptionHandler; +import de.deadlocker8.budgetmaster.logic.Helpers; +import de.deadlocker8.budgetmaster.logic.MonthInOutSum; +import de.deadlocker8.budgetmaster.logic.ServerConnection; +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.geometry.Insets; +import javafx.scene.chart.LineChart; +import javafx.scene.control.Accordion; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.Button; +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.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.util.Callback; +import logger.Logger; +import tools.AlertGenerator; public class ChartController implements Refreshable { @FXML private AnchorPane anchorPaneMain; + @FXML private Accordion accordion; + @FXML private DatePicker datePickerStart; + @FXML private VBox vboxChartCategories; + @FXML private DatePicker datePickerEnd; + @FXML private VBox vboxChartMonth; + @FXML private Button buttonChartCategoriesShow; + @FXML private ComboBox<String> comboBoxStartMonth; + @FXML private ComboBox<String> comboBoxStartYear; + @FXML private ComboBox<String> comboBoxEndMonth; + @FXML private ComboBox<String> comboBoxEndYear; + @FXML private Button buttonChartMonthShow; + @FXML private RadioButton radioButtonBars; + @FXML private RadioButton radioButtonLines; private Controller controller; @@ -14,12 +64,165 @@ public class ChartController implements Refreshable this.controller = controller; anchorPaneMain.setStyle("-fx-background-color: #F4F4F4;"); + vboxChartCategories.setStyle("-fx-background-color: #F4F4F4;"); + vboxChartCategories.setSpacing(20); + 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); + buttonChartMonthShow.setStyle("-fx-background-color: #2E79B9;"); + buttonChartMonthShow.setGraphic(iconShow2); + + datePickerEnd.setDayCellFactory(new Callback<DatePicker, DateCell>() + { + @Override + public DateCell call(DatePicker param) + { + return new DateCell() + { + @Override + public void updateItem(LocalDate item, boolean empty) + { + super.updateItem(item, empty); + if(item.isBefore(datePickerStart.getValue().plusDays(1))) + { + setDisable(true); + setStyle("-fx-background-color: #ffc0cb;"); + } + } + }; + } + }); + + comboBoxStartMonth.setItems(FXCollections.observableArrayList(Helpers.getMonthList())); + comboBoxStartMonth.setValue(controller.getCurrentDate().minusMonths(5).toString("MMMM")); + + comboBoxStartYear.setItems(FXCollections.observableArrayList(Helpers.getYearList())); + comboBoxStartYear.setValue(String.valueOf(controller.getCurrentDate().minusMonths(5).getYear())); + + comboBoxEndMonth.setItems(FXCollections.observableArrayList(Helpers.getMonthList())); + comboBoxEndMonth.setValue(controller.getCurrentDate().plusMonths(6).toString("MMMM")); + + comboBoxEndYear.setItems(FXCollections.observableArrayList(Helpers.getYearList())); + comboBoxEndYear.setValue(String.valueOf(controller.getCurrentDate().plusMonths(6).getYear())); + + final ToggleGroup toggleGroup = new ToggleGroup(); + radioButtonBars.setToggleGroup(toggleGroup); + radioButtonBars.setSelected(true); + radioButtonLines.setToggleGroup(toggleGroup); + + accordion.setExpandedPane(accordion.getPanes().get(0)); + vboxChartMonth.setSpacing(15); + } + + public void chartCategoriesShow() + { + DateTime startDate = DateTime.parse(datePickerStart.getValue().toString()); + DateTime endDate = DateTime.parse(datePickerEnd.getValue().toString()); + + try + { + ServerConnection connection = new ServerConnection(controller.getSettings()); + ArrayList<CategoryInOutSum> sums = connection.getCategoryInOutSumForMonth(startDate, endDate); + + vboxChartCategories.getChildren().clear(); + + CategoriesChartGenerator generator = new CategoriesChartGenerator("Einnahmen nach Kategorien", sums, true, controller.getSettings().getCurrency()); + vboxChartCategories.getChildren().add(generator.generate()); + generator = new CategoriesChartGenerator("Ausgaben nach Kategorien", sums, false, controller.getSettings().getCurrency()); + vboxChartCategories.getChildren().add(generator.generate()); + + Region spacer = new Region(); + vboxChartCategories.getChildren().add(spacer); + VBox.setVgrow(spacer, Priority.ALWAYS); + + vboxChartCategories.getChildren().add(generator.generateLegend()); + } + catch(Exception e) + { + Logger.error(e); + controller.showConnectionErrorAlert(ExceptionHandler.getMessageForException(e)); + } + } + + public void chartMonthShow() + { + vboxChartMonth.getChildren().clear(); + + String startMonth = comboBoxStartMonth.getValue(); + String startYear = comboBoxStartYear.getValue(); + String endMonth = comboBoxEndMonth.getValue(); + String endYear = comboBoxEndYear.getValue(); + + String startDateString = "01-" + startMonth + "-" + startYear; + DateTime startDate = DateTime.parse(startDateString, DateTimeFormat.forPattern("dd-MMMM-YYYY")); + + String endDateString = "01-" + endMonth + "-" + endYear; + DateTime endDate = DateTime.parse(endDateString, DateTimeFormat.forPattern("dd-MMMM-YYYY")); + + if(endDate.isBefore(startDate)) + { + AlertGenerator.showAlert(AlertType.WARNING, "Warnung", "", "Das Enddatum darf nicht vor dem Startdatum liegen.", controller.getIcon(), controller.getStage(), null, false); + return; + } + + try + { + ServerConnection connection = new ServerConnection(controller.getSettings()); + ArrayList<MonthInOutSum> sums = connection.getMonthInOutSum(startDate, endDate); + + vboxChartMonth.getChildren().clear(); + + if(radioButtonBars.isSelected()) + { + 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 + { + 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); + controller.showConnectionErrorAlert(ExceptionHandler.getMessageForException(e)); + } } @Override public void refresh() { - //TODO Auto-generated method stub - + // chart categories + LocalDate startDate = LocalDate.parse(controller.getCurrentDate().withDayOfMonth(1).toString("yyyy-MM-dd")); + LocalDate endDate = LocalDate.parse(controller.getCurrentDate().dayOfMonth().withMaximumValue().toString("yyy-MM-dd")); + + datePickerStart.setValue(startDate); + datePickerEnd.setValue(endDate); + + chartCategoriesShow(); + + // chart month + chartMonthShow(); } } \ 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 e4461f90a25c86b4af4d245e8fd3e8d015bb083f..7ffa60128c56a3f29617e506560b0eef6f6963ad 100644 --- a/src/de/deadlocker8/budgetmaster/ui/ChartTab.fxml +++ b/src/de/deadlocker8/budgetmaster/ui/ChartTab.fxml @@ -1,10 +1,141 @@ <?xml version="1.0" encoding="UTF-8"?> +<?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.RadioButton?> +<?import javafx.scene.control.TitledPane?> <?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.VBox?> +<?import javafx.scene.text.Font?> <AnchorPane fx:id="anchorPaneMain" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="de.deadlocker8.budgetmaster.ui.ChartController"> <children> - <VBox alignment="TOP_CENTER" layoutY="24.0" prefHeight="562.0" prefWidth="772.0" spacing="25.0" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="25.0" /> + <Accordion fx:id="accordion" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="25.0"> + <panes> + <TitledPane animated="false" text="Einnahmen/Ausgaben nach Kategorien"> + <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> + <DatePicker fx:id="datePickerStart" /> + </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> + <DatePicker fx:id="datePickerEnd" /> + <Button fx:id="buttonChartCategoriesShow" 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> + <VBox fx:id="vboxChartCategories" VBox.vgrow="ALWAYS"> + <VBox.margin> + <Insets /> + </VBox.margin> + <padding> + <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" /> + </padding></VBox> + </children> + </VBox> + </content> + <font> + <Font name="System Bold" size="12.0" /> + </font> + </TitledPane> + <TitledPane animated="false" text="Einnahmen/Ausgaben pro Monat"> + <font> + <Font name="System Bold" size="12.0" /> + </font> + <content> + <VBox spacing="20.0"> + <children> + <HBox alignment="CENTER_LEFT" prefHeight="8.0" prefWidth="750.0" spacing="50.0"> + <children> + <VBox spacing="15.0"> + <children> + <HBox alignment="CENTER_RIGHT" spacing="10.0"> + <children> + <Label prefHeight="25.0" prefWidth="45.0" text="Von:"> + <font> + <Font name="System Bold" size="16.0" /> + </font> + </Label> + <ComboBox fx:id="comboBoxStartMonth" prefHeight="25.0" prefWidth="110.0" /> + <ComboBox fx:id="comboBoxStartYear" prefWidth="70.0" /> + </children> + </HBox> + <HBox alignment="CENTER_LEFT" spacing="10.0"> + <children> + <Label prefWidth="45.0" text="Bis:"> + <font> + <Font name="System Bold" size="16.0" /> + </font> + </Label> + <ComboBox fx:id="comboBoxEndMonth" prefWidth="110.0" /> + <ComboBox fx:id="comboBoxEndYear" prefWidth="70.0" /> + </children> + </HBox> + </children> + </VBox> + <HBox alignment="CENTER_LEFT" spacing="15.0" HBox.hgrow="ALWAYS"> + <children> + <RadioButton fx:id="radioButtonBars" mnemonicParsing="false" text="Balken"> + <font> + <Font size="14.0" /> + </font></RadioButton> + <RadioButton fx:id="radioButtonLines" mnemonicParsing="false" text="Linien"> + <font> + <Font size="14.0" /> + </font></RadioButton> + <Button fx:id="buttonChartMonthShow" mnemonicParsing="false" onAction="#chartMonthShow"> + <font> + <Font name="System Bold" size="12.0" /> + </font> + <HBox.margin> + <Insets left="10.0" /> + </HBox.margin> + </Button> + </children> + </HBox> + </children> + </HBox> + <VBox fx:id="vboxChartMonth" prefHeight="200.0" prefWidth="100.0" VBox.vgrow="ALWAYS" /> + </children> + </VBox> + </content> + </TitledPane> + </panes> + </Accordion> </children> </AnchorPane> diff --git a/src/de/deadlocker8/budgetmaster/ui/Controller.java b/src/de/deadlocker8/budgetmaster/ui/Controller.java index 0c7c7cbf73a750e58bcba1ea941896bd5f470cc6..408d23943441fc9ab15b41c4f58e3cae847b9362 100644 --- a/src/de/deadlocker8/budgetmaster/ui/Controller.java +++ b/src/de/deadlocker8/budgetmaster/ui/Controller.java @@ -9,6 +9,7 @@ import org.joda.time.DateTime; import de.deadlocker8.budgetmaster.logic.CategoryBudget; import de.deadlocker8.budgetmaster.logic.CategoryHandler; +import de.deadlocker8.budgetmaster.logic.ExceptionHandler; import de.deadlocker8.budgetmaster.logic.FilterSettings; import de.deadlocker8.budgetmaster.logic.NormalPayment; import de.deadlocker8.budgetmaster.logic.PaymentHandler; @@ -104,9 +105,7 @@ public class Controller Parent nodeTabChart = (Parent)fxmlLoader.load(); chartController = fxmlLoader.getController(); chartController.init(this); - tabCharts.setContent(nodeTabChart); - //TODO - tabCharts.setDisable(true); + tabCharts.setContent(nodeTabChart); fxmlLoader = new FXMLLoader(getClass().getResource("/de/deadlocker8/budgetmaster/ui/SettingsTab.fxml")); Parent nodeTabSettings = (Parent)fxmlLoader.load(); @@ -144,6 +143,7 @@ public class Controller { Platform.runLater(() -> { AlertGenerator.showAlert(AlertType.WARNING, "Warnung", "", "Bitte gibt zuerst deine Serverdaten ein!", icon, stage, null, false); + toggleAllTabsExceptSettings(true); tabPane.getSelectionModel().select(tabSettings); }); } @@ -228,23 +228,34 @@ public class Controller return currentDate; } - public void showConnectionErrorAlert() - { + public void showConnectionErrorAlert(String errorMessage) + { if(!alertIsShowing) { + alertIsShowing = true; Platform.runLater(() -> { + toggleAllTabsExceptSettings(true); + tabPane.getSelectionModel().select(tabSettings); + alertIsShowing = true; Alert alert = new Alert(AlertType.ERROR); alert.setTitle("Fehler"); alert.setHeaderText(""); - alert.setContentText("Beim Herstellen der Verbindung zum Server ist ein Fehler aufgetreten. Bitte überprüfe deine Einstellungen und ob der Server läuft."); + if(errorMessage == null) + { + alert.setContentText("Beim Herstellen der Verbindung zum Server ist ein Fehler aufgetreten. Bitte überprüfe deine Einstellungen."); + } + else + { + alert.setContentText("Beim Herstellen der Verbindung zum Server ist ein Fehler aufgetreten. Bitte überprüfe deine Einstellungen.\n\n" + + "Fehlerdetails:\n" + errorMessage); + } + Stage dialogStage = (Stage)alert.getDialogPane().getScene().getWindow(); dialogStage.getIcons().add(icon); dialogStage.initOwner(stage); - dialogStage.setOnCloseRequest((event) -> { - alertIsShowing = false; - }); alert.showAndWait(); + alertIsShowing = false; }); } } @@ -281,6 +292,14 @@ public class Controller { this.filterSettings = filterSettings; } + + public void toggleAllTabsExceptSettings(boolean disable) + { + tabHome.setDisable(disable); + tabPayments.setDisable(disable); + tabCategories.setDisable(disable); + tabCharts.setDisable(disable); + } public void about() { @@ -291,7 +310,7 @@ public class Controller { try { - ServerConnection connection = new ServerConnection(settings); + ServerConnection connection = new ServerConnection(settings); paymentHandler = new PaymentHandler(); paymentHandler.getPayments().addAll(connection.getPayments(currentDate.getYear(), currentDate.getMonthOfYear())); @@ -308,12 +327,14 @@ public class Controller categoryBudgets = connection.getCategoryBudgets(currentDate.getYear(), currentDate.getMonthOfYear()); paymentHandler.filter(newFilterSettings); + + toggleAllTabsExceptSettings(false); } catch(Exception e) { Logger.error(e); - categoryHandler = new CategoryHandler(null); - showConnectionErrorAlert(); + categoryHandler = new CategoryHandler(null); + showConnectionErrorAlert(ExceptionHandler.getMessageForException(e)); } refreshAllTabs(); diff --git a/src/de/deadlocker8/budgetmaster/ui/HomeController.java b/src/de/deadlocker8/budgetmaster/ui/HomeController.java index ab2bb352069d6ad6a76237b90cd55989d57e64f4..a17c5d1effb508eaa3c5b2fafacf6e9ca34024ed 100644 --- a/src/de/deadlocker8/budgetmaster/ui/HomeController.java +++ b/src/de/deadlocker8/budgetmaster/ui/HomeController.java @@ -77,7 +77,12 @@ public class HomeController implements Refreshable { Budget budget = new Budget(controller.getPaymentHandler().getPayments()); double remaining = budget.getIncomeSum() + budget.getPaymentSum(); - labelBudget.setText(String.valueOf(Helpers.NUMBER_FORMAT.format(remaining).replace(".", ",")) + " " + controller.getSettings().getCurrency()); + String currency = "€"; + if(controller.getSettings() != null) + { + currency = controller.getSettings().getCurrency(); + } + labelBudget.setText(String.valueOf(Helpers.NUMBER_FORMAT.format(remaining).replace(".", ",")) + " " + currency); if(remaining <= 0) { labelBudget.setStyle("-fx-text-fill: #CC0000"); @@ -86,7 +91,7 @@ public class HomeController implements Refreshable { labelBudget.setStyle("-fx-text-fill: " + controller.getBundle().getString("color.text")); } - labelStartBudget.setText("von " + String.valueOf(Helpers.NUMBER_FORMAT.format(budget.getIncomeSum()).replace(".", ",")) + " " + controller.getSettings().getCurrency() + " verbleibend"); + labelStartBudget.setText("von " + String.valueOf(Helpers.NUMBER_FORMAT.format(budget.getIncomeSum()).replace(".", ",")) + " " + currency + " verbleibend"); double factor = remaining / budget.getIncomeSum(); if(factor < 0) diff --git a/src/de/deadlocker8/budgetmaster/ui/Modal.fxml b/src/de/deadlocker8/budgetmaster/ui/Modal.fxml new file mode 100644 index 0000000000000000000000000000000000000000..33759ff9bb89cf861f75e900f957f48ee27a25fd --- /dev/null +++ b/src/de/deadlocker8/budgetmaster/ui/Modal.fxml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.scene.control.Label?> +<?import javafx.scene.control.ProgressIndicator?> +<?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.text.Font?> + +<AnchorPane fx:id="anchorPaneMain" prefHeight="100.0" prefWidth="375.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="de.deadlocker8.budgetmaster.ui.ModalController"> + <children> + <HBox alignment="CENTER_LEFT" spacing="10.0" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="14.0"> + <children> + <Label fx:id="labelMessage" maxWidth="1.7976931348623157E308" HBox.hgrow="ALWAYS"> + <font> + <Font size="15.0" /> + </font> + </Label> + <ProgressIndicator prefHeight="72.0" prefWidth="34.0" /> + </children> + </HBox> + </children> +</AnchorPane> diff --git a/src/de/deadlocker8/budgetmaster/ui/ModalController.java b/src/de/deadlocker8/budgetmaster/ui/ModalController.java new file mode 100644 index 0000000000000000000000000000000000000000..042c7345a46f1f688df26dc19bf4eaf82a317c42 --- /dev/null +++ b/src/de/deadlocker8/budgetmaster/ui/ModalController.java @@ -0,0 +1,18 @@ +package de.deadlocker8.budgetmaster.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.stage.Stage; + +public class ModalController +{ + @FXML private Label labelMessage; + + public void init(Stage stage, String message) + { + labelMessage.setText(message); + stage.setOnCloseRequest((e)->{ + e.consume(); + }); + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/ui/NewCategoryController.java b/src/de/deadlocker8/budgetmaster/ui/NewCategoryController.java index 4a94063bfc834cc950c48a9956a696740eb65fb2..d0ab8dd13f7a575c5765ed274ec99d023d3b1534 100644 --- a/src/de/deadlocker8/budgetmaster/ui/NewCategoryController.java +++ b/src/de/deadlocker8/budgetmaster/ui/NewCategoryController.java @@ -6,6 +6,7 @@ import org.controlsfx.control.PopOver; import org.controlsfx.control.PopOver.ArrowLocation; import de.deadlocker8.budgetmaster.logic.Category; +import de.deadlocker8.budgetmaster.logic.ExceptionHandler; import de.deadlocker8.budgetmaster.logic.ServerConnection; import fontAwesome.FontIcon; import fontAwesome.FontIconType; @@ -135,6 +136,12 @@ public class NewCategoryController return; } + if(name.length() > 45) + { + AlertGenerator.showAlert(AlertType.WARNING, "Warnung", "", "Der Name darf maximal 45 Zeichen lang sein.", controller.getIcon(), controller.getStage(), null, false); + return; + } + if(edit) { category.setName(name); @@ -147,7 +154,7 @@ public class NewCategoryController } catch(Exception e) { - controller.showConnectionErrorAlert(); + controller.showConnectionErrorAlert(ExceptionHandler.getMessageForException(e)); } } else @@ -161,7 +168,7 @@ public class NewCategoryController } catch(Exception e) { - controller.showConnectionErrorAlert(); + controller.showConnectionErrorAlert(e.getMessage()); } } diff --git a/src/de/deadlocker8/budgetmaster/ui/NewCategoryGUI.fxml b/src/de/deadlocker8/budgetmaster/ui/NewCategoryGUI.fxml index 9a6736f6a807a48ae2359b47ada1e91e2bfb6277..203d4acb88da4931ec70ca307d61d331d061861c 100644 --- a/src/de/deadlocker8/budgetmaster/ui/NewCategoryGUI.fxml +++ b/src/de/deadlocker8/budgetmaster/ui/NewCategoryGUI.fxml @@ -15,11 +15,20 @@ <children> <HBox alignment="CENTER_LEFT" prefHeight="30.0" prefWidth="465.0"> <children> - <Label prefHeight="29.0" prefWidth="125.0" text="Name:"> - <font> - <Font name="System Bold" size="14.0" /> - </font> - </Label> + <VBox> + <children> + <Label prefHeight="29.0" prefWidth="125.0" text="Name:"> + <font> + <Font name="System Bold" size="14.0" /> + </font> + </Label> + <Label prefHeight="29.0" prefWidth="125.0" text="(max. 45 Zeichen)"> + <font> + <Font size="11.0" /> + </font> + </Label> + </children> + </VBox> <TextField fx:id="textFieldName" prefHeight="29.0" HBox.hgrow="ALWAYS"> <font> <Font size="13.0" /> diff --git a/src/de/deadlocker8/budgetmaster/ui/NewPaymentController.java b/src/de/deadlocker8/budgetmaster/ui/NewPaymentController.java index 51355b5c2be6edac9a67530244c1cdb6299c48b4..1cdd2f05dc59b1c6400127ab386abcfa60e34f2b 100644 --- a/src/de/deadlocker8/budgetmaster/ui/NewPaymentController.java +++ b/src/de/deadlocker8/budgetmaster/ui/NewPaymentController.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import org.joda.time.DateTime; import de.deadlocker8.budgetmaster.logic.Category; +import de.deadlocker8.budgetmaster.logic.ExceptionHandler; import de.deadlocker8.budgetmaster.logic.Helpers; import de.deadlocker8.budgetmaster.logic.NormalPayment; import de.deadlocker8.budgetmaster.logic.Payment; @@ -17,7 +18,6 @@ import de.deadlocker8.budgetmaster.ui.cells.RepeatingDayCell; import de.deadlocker8.budgetmaster.ui.cells.SmallCategoryCell; import fontAwesome.FontIcon; import fontAwesome.FontIconType; -import javafx.application.Platform; import javafx.fxml.FXML; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Button; @@ -107,6 +107,7 @@ public class NewPaymentController comboBoxCategory.setCellFactory((view) -> { return new SmallCategoryCell(); }); + comboBoxRepeatingDay.setValue(1); buttonCategoryCell = new ButtonCategoryCell(Color.WHITE); comboBoxCategory.setButtonCell(buttonCategoryCell); comboBoxCategory.setStyle("-fx-border-color: #000000; -fx-border-width: 2; -fx-border-radius: 5; -fx-background-radius: 5;"); @@ -138,7 +139,7 @@ public class NewPaymentController } catch(Exception e) { - controller.showConnectionErrorAlert(); + controller.showConnectionErrorAlert(ExceptionHandler.getMessageForException(e)); stage.close(); return; } @@ -213,9 +214,18 @@ public class NewPaymentController toggleRepeatingArea(false); //preselect correct month and year - DateTime currentDate = controller.getCurrentDate(); - datePicker.setValue(LocalDate.now().withYear(currentDate.getYear()).withMonth(currentDate.getMonthOfYear()).withDayOfMonth(currentDate.getDayOfMonth())); - Platform.runLater(()->{datePicker.getEditor().clear();}); + DateTime currentDate = controller.getCurrentDate(); + if(DateTime.now().getDayOfMonth() > currentDate.dayOfMonth().withMaximumValue().getDayOfMonth()) + { + currentDate = currentDate.dayOfMonth().withMaximumValue(); + } + + LocalDate currentLocalDate = LocalDate.now().withYear(currentDate.getYear()) + .withMonth(currentDate.getMonthOfYear()) + .withDayOfMonth(currentDate.getDayOfMonth()); + datePicker.setValue(currentLocalDate); + datePicker.setEditable(false); + //Platform.runLater(()->{datePicker.getEditor().clear();}); } } @@ -227,6 +237,12 @@ public class NewPaymentController AlertGenerator.showAlert(AlertType.WARNING, "Warnung", "", "Das Feld für den Namen darf nicht leer sein.", controller.getIcon(), controller.getStage(), null, false); return; } + + if(name.length() > 150) + { + AlertGenerator.showAlert(AlertType.WARNING, "Warnung", "", "Der Name darf maximal 150 Zeichen lang sein.", controller.getIcon(), controller.getStage(), null, false); + return; + } String amountText = textFieldAmount.getText(); if(!amountText.matches("^-?\\d+(,\\d+)*(\\.\\d+(e\\d+)?)?$")) @@ -308,7 +324,7 @@ public class NewPaymentController catch(Exception e) { Logger.error(e); - controller.showConnectionErrorAlert(); + controller.showConnectionErrorAlert(ExceptionHandler.getMessageForException(e)); } } else @@ -322,7 +338,7 @@ public class NewPaymentController catch(Exception e) { Logger.error(e); - controller.showConnectionErrorAlert(); + controller.showConnectionErrorAlert(e.getMessage()); } } } @@ -348,7 +364,7 @@ public class NewPaymentController catch(Exception e) { Logger.error(e); - controller.showConnectionErrorAlert(); + controller.showConnectionErrorAlert(e.getMessage()); } } else @@ -362,7 +378,7 @@ public class NewPaymentController catch(Exception e) { Logger.error(e); - controller.showConnectionErrorAlert(); + controller.showConnectionErrorAlert(e.getMessage()); } } } diff --git a/src/de/deadlocker8/budgetmaster/ui/NewPaymentGUI.fxml b/src/de/deadlocker8/budgetmaster/ui/NewPaymentGUI.fxml index 9235231a6402291bd989eb171cb5cfe6d0d201aa..31366e41dd3bded01be30e3b15e6ab06cdce95be 100644 --- a/src/de/deadlocker8/budgetmaster/ui/NewPaymentGUI.fxml +++ b/src/de/deadlocker8/budgetmaster/ui/NewPaymentGUI.fxml @@ -22,11 +22,20 @@ <children> <HBox alignment="CENTER_LEFT" prefHeight="30.0" prefWidth="465.0"> <children> - <Label prefHeight="29.0" prefWidth="125.0" text="Name:"> - <font> - <Font name="System Bold" size="14.0" /> - </font> - </Label> + <VBox> + <children> + <Label prefHeight="29.0" prefWidth="125.0" text="Name:"> + <font> + <Font name="System Bold" size="14.0" /> + </font> + </Label> + <Label prefHeight="29.0" prefWidth="125.0" text="(max. 150 Zeichen)"> + <font> + <Font size="11.0" /> + </font> + </Label> + </children> + </VBox> <TextField fx:id="textFieldName" prefHeight="29.0" HBox.hgrow="ALWAYS"> <font> <Font size="13.0" /> diff --git a/src/de/deadlocker8/budgetmaster/ui/PaymentController.java b/src/de/deadlocker8/budgetmaster/ui/PaymentController.java index db5adcc84d3d952eae84f269b1acce9e1e9d976e..c4b7cc52c7d3ea25d3030b59c9c2c4c165637487 100644 --- a/src/de/deadlocker8/budgetmaster/ui/PaymentController.java +++ b/src/de/deadlocker8/budgetmaster/ui/PaymentController.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.util.ArrayList; import de.deadlocker8.budgetmaster.logic.Budget; +import de.deadlocker8.budgetmaster.logic.ExceptionHandler; import de.deadlocker8.budgetmaster.logic.FilterSettings; import de.deadlocker8.budgetmaster.logic.Helpers; import de.deadlocker8.budgetmaster.logic.NormalPayment; @@ -74,6 +75,7 @@ public class PaymentController implements Refreshable } } }); + cell.prefWidthProperty().bind(listView.widthProperty().subtract(2)); return cell; } }); @@ -204,7 +206,7 @@ public class PaymentController implements Refreshable catch(Exception e) { e.printStackTrace(); - controller.showConnectionErrorAlert(); + controller.showConnectionErrorAlert(ExceptionHandler.getMessageForException(e)); } } @@ -219,7 +221,7 @@ public class PaymentController implements Refreshable catch(Exception e) { e.printStackTrace(); - controller.showConnectionErrorAlert(); + controller.showConnectionErrorAlert(ExceptionHandler.getMessageForException(e)); } } @@ -238,7 +240,7 @@ public class PaymentController implements Refreshable catch(Exception e) { e.printStackTrace(); - controller.showConnectionErrorAlert(); + controller.showConnectionErrorAlert(ExceptionHandler.getMessageForException(e)); } } diff --git a/src/de/deadlocker8/budgetmaster/ui/SettingsController.java b/src/de/deadlocker8/budgetmaster/ui/SettingsController.java index 471619dc9dbe1c4788c6de5b70a64e7354e13d9f..76e98b0784ff7d04fcf25ccf4e5e9b44328b4803 100644 --- a/src/de/deadlocker8/budgetmaster/ui/SettingsController.java +++ b/src/de/deadlocker8/budgetmaster/ui/SettingsController.java @@ -1,19 +1,43 @@ package de.deadlocker8.budgetmaster.ui; +import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.Optional; +import de.deadlocker8.budgetmaster.logic.ExceptionHandler; +import de.deadlocker8.budgetmaster.logic.Helpers; +import de.deadlocker8.budgetmaster.logic.ServerConnection; import de.deadlocker8.budgetmaster.logic.Settings; import de.deadlocker8.budgetmaster.logic.Utils; +import de.deadlocker8.budgetmasterserver.logic.Database; +import javafx.application.Platform; import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +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.ButtonBar.ButtonData; +import javafx.scene.control.ButtonType; import javafx.scene.control.Label; import javafx.scene.control.RadioButton; +import javafx.scene.control.TextArea; import javafx.scene.control.TextField; +import javafx.scene.control.TextInputDialog; import javafx.scene.control.ToggleGroup; import javafx.scene.layout.AnchorPane; +import javafx.stage.FileChooser; +import javafx.stage.Modality; +import javafx.stage.Stage; import logger.Logger; import tools.AlertGenerator; +import tools.BASE58Type; +import tools.ConvertTo; +import tools.HashUtils; +import tools.RandomCreations; +import tools.Worker; public class SettingsController { @@ -25,8 +49,12 @@ public class SettingsController @FXML private TextField textFieldCurrency; @FXML private Label labelCurrency; @FXML private Button buttonSave; + @FXML private Button buttonExportDB; + @FXML private Button buttonImportDB; + @FXML private Button buttonDeleteDB; @FXML private RadioButton radioButtonRestActivated; @FXML private RadioButton radioButtonRestDeactivated; + @FXML private TextArea textAreaTrustedHosts; private Controller controller; @@ -36,7 +64,7 @@ public class SettingsController if(controller.getSettings() != null) { textFieldURL.setText(controller.getSettings().getUrl()); - textFieldSecret.setText(controller.getSettings().getSecret()); + textFieldSecret.setText("******"); textFieldCurrency.setText(controller.getSettings().getCurrency()); if(controller.getSettings().isRestActivated()) { @@ -46,74 +74,326 @@ public class SettingsController { radioButtonRestDeactivated.setSelected(true); } + setTextAreaTrustedHosts(controller.getSettings().getTrustedHosts()); } - + anchorPaneMain.setStyle("-fx-background-color: #F4F4F4;"); labelSecret.setStyle("-fx-text-fill: " + controller.getBundle().getString("color.text")); labelURL.setStyle("-fx-text-fill: " + controller.getBundle().getString("color.text")); labelCurrency.setStyle("-fx-text-fill: " + controller.getBundle().getString("color.text")); - buttonSave.setStyle("-fx-background-color: #2E79B9; -fx-text-fill: white; -fx-font-weight: bold; -fx-font-size: 16;"); + buttonSave.setStyle("-fx-background-color: #2E79B9; -fx-text-fill: white; -fx-font-weight: bold; -fx-font-size: 16;"); + buttonExportDB.setStyle("-fx-background-color: #2E79B9; -fx-text-fill: white; -fx-font-weight: bold; -fx-font-size: 14;"); + buttonImportDB.setStyle("-fx-background-color: #2E79B9; -fx-text-fill: white; -fx-font-weight: bold; -fx-font-size: 14;"); + buttonDeleteDB.setStyle("-fx-background-color: #FF5047; -fx-text-fill: white; -fx-font-weight: bold; -fx-font-size: 14;"); textFieldURL.setPromptText("z.B. https://yourdomain.de"); textFieldCurrency.setPromptText("z.B. €, CHF, $"); - + textAreaTrustedHosts.setPromptText("z.B. localhost"); + ToggleGroup toggleGroup = new ToggleGroup(); radioButtonRestActivated.setToggleGroup(toggleGroup); radioButtonRestDeactivated.setToggleGroup(toggleGroup); } - + + private void setTextAreaTrustedHosts(ArrayList<String> trustedHosts) + { + StringBuilder trustedHostsString = new StringBuilder(); + if(trustedHosts != null) + { + for(String currentHost : trustedHosts) + { + trustedHostsString.append(currentHost); + trustedHostsString.append("\n"); + } + textAreaTrustedHosts.setText(trustedHostsString.toString()); + } + else + { + textAreaTrustedHosts.setText(""); + } + } + public void save() { String url = textFieldURL.getText().trim(); String secret = textFieldSecret.getText().trim(); String currency = textFieldCurrency.getText().trim(); - if(url != null && !url.equals("")) + if(url == null || url.equals("")) + { + AlertGenerator.showAlert(AlertType.WARNING, "Warnung", "", "Das Feld für die Server URL darf nicht leer sein!", controller.getIcon(), controller.getStage(), null, false); + return; + } + + if(secret == null || secret.equals("")) + { + AlertGenerator.showAlert(AlertType.WARNING, "Warnung", "", "Das Server Passwortfeld darf nicht leer sein!", controller.getIcon(), controller.getStage(), null, false); + return; + } + + if(currency == null || currency.equals("")) + { + AlertGenerator.showAlert(AlertType.WARNING, "Warnung", "", "Bitte gib deine gewünschte Währung ein!", controller.getIcon(), controller.getStage(), null, false); + return; + } + + ArrayList<String> trustedHosts = new ArrayList<>(); + String trustedHostText = textAreaTrustedHosts.getText(); + String[] trustedHostsArray = trustedHostText.split("\n"); + for(String currentHost : trustedHostsArray) + { + currentHost = currentHost.trim(); + if(!currentHost.equals("")) + { + trustedHosts.add(currentHost); + } + } + setTextAreaTrustedHosts(trustedHosts); + + if(controller.getSettings() != null) { - if(secret != null && !secret.equals("")) + if(!secret.equals("******")) { - if(currency != null && !currency.equals("")) + controller.getSettings().setSecret(HashUtils.hash(secret, Helpers.SALT)); + } + controller.getSettings().setUrl(url); + controller.getSettings().setCurrency(currency); + controller.getSettings().setRestActivated(radioButtonRestActivated.isSelected()); + controller.getSettings().setTrustedHosts(trustedHosts); + } + else + { + Settings settings = new Settings(); + settings.setUrl(url); + settings.setSecret(HashUtils.hash(secret, Helpers.SALT)); + settings.setCurrency(currency); + settings.setRestActivated(radioButtonRestActivated.isSelected()); + settings.setTrustedHosts(trustedHosts); + controller.setSettings(settings); + } + + try + { + Utils.saveSettings(controller.getSettings()); + } + catch(IOException e) + { + Logger.error(e); + AlertGenerator.showAlert(AlertType.ERROR, "Fehler", "", "Beim Speichern der Einstellungen ist ein Fehler aufgetreten", controller.getIcon(), controller.getStage(), null, false); + } + + textFieldSecret.setText("******"); + + controller.refresh(controller.getFilterSettings()); + controller.showNotification("Erfolgreich gespeichert"); + } + + public void exportDB() + { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Datenbank exportieren"); + FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("JSON (*.json)", "*.json"); + fileChooser.getExtensionFilters().add(extFilter); + File file = fileChooser.showSaveDialog(controller.getStage()); + if(file != null) + { + Stage modalStage = showModal("Vorgang läuft", "Die Datenbank wird exportiert, bitte warten..."); + + Worker.runLater(() -> { + try { - if(controller.getSettings() != null) - { - controller.getSettings().setUrl(url); - controller.getSettings().setSecret(secret); - controller.getSettings().setCurrency(currency); - controller.getSettings().setRestActivated(radioButtonRestActivated.isSelected()); - } - else - { - Settings settings = new Settings(); - settings.setUrl(url); - settings.setSecret(secret); - settings.setCurrency(currency); - settings.setRestActivated(radioButtonRestActivated.isSelected()); - controller.setSettings(settings); - } - + ServerConnection connection = new ServerConnection(controller.getSettings()); + String databaseJSON = connection.exportDatabase(); + Utils.saveDatabaseJSON(file, databaseJSON); + + Platform.runLater(() -> { + if(modalStage != null) + { + modalStage.close(); + } + AlertGenerator.showAlert(AlertType.INFORMATION, "Erfolgreich exportiert", "", "Die Datenbank wurder erfolgreich exportiert.", controller.getIcon(), controller.getStage(), null, false); + }); + } + catch(Exception e) + { + Logger.error(e); + Platform.runLater(() -> { + if(modalStage != null) + { + modalStage.close(); + } + controller.showConnectionErrorAlert(ExceptionHandler.getMessageForException(e)); + }); + } + }); + } + } + + private void importDatabase() + { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Datenbank importieren"); + FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("JSON (*.json)", "*.json"); + fileChooser.getExtensionFilters().add(extFilter); + File file = fileChooser.showOpenDialog(controller.getStage()); + if(file != null) + { + Database database; + try + { + database = Utils.loadDatabaseJSON(file); + if(database.getCategories() == null || database.getNormalPayments() == null || database.getRepeatingPayments() == null) + { + AlertGenerator.showAlert(AlertType.ERROR, "Fehler", "", "Die angegebene Datei enthält kein gültiges BudgetMaster-Datenformat und kann daher nicht importiert werden.", controller.getIcon(), controller.getStage(), null, false); + return; + } + } + catch(IOException e1) + { + Logger.error(e1); + AlertGenerator.showAlert(AlertType.ERROR, "Fehler", "", "Beim Einlesen der Datei ist ein Fehler aufgetreten.", controller.getIcon(), controller.getStage(), null, false); + return; + } + + Stage modalStage = showModal("Vorgang läuft", "Die Datenbank wird importiert, bitte warten..."); + + Worker.runLater(() -> { + try + { + ServerConnection connection = new ServerConnection(controller.getSettings()); + connection.importDatabase(database); + + Platform.runLater(() -> { + if(modalStage != null) + { + modalStage.close(); + } + AlertGenerator.showAlert(AlertType.INFORMATION, "Erfolgreich exportiert", "", "Die Datenbank wurder erfolgreich importiert.", controller.getIcon(), controller.getStage(), null, false); + }); + } + catch(Exception e) + { + Logger.error(e); + Platform.runLater(() -> { + if(modalStage != null) + { + modalStage.close(); + } + controller.showConnectionErrorAlert(ExceptionHandler.getMessageForException(e)); + }); + } + }); + } + } + + public void importDB() + { + Alert alert = new Alert(AlertType.CONFIRMATION); + alert.setTitle("Datenbank importieren"); + alert.setHeaderText(""); + alert.setContentText("Soll die Datenbank vor dem Importieren gelöscht werden?"); + Stage dialogStage = (Stage)alert.getDialogPane().getScene().getWindow(); + dialogStage.getIcons().add(controller.getIcon()); + dialogStage.initOwner(controller.getStage()); + + ButtonType buttonTypeDelete = new ButtonType("Ja, Datenbank löschen"); + ButtonType buttonTypeAppend = new ButtonType("Nein, Daten hinzufügen"); + ButtonType buttonTypeCancel = new ButtonType("Abbrechen", ButtonData.CANCEL_CLOSE); + + alert.getButtonTypes().setAll(buttonTypeDelete, buttonTypeAppend, buttonTypeCancel); + Optional<ButtonType> result = alert.showAndWait(); + if(result.get() == buttonTypeDelete) + { + deleteDatabase(true); + } + else if(result.get() == buttonTypeAppend) + { + importDatabase(); + } + } + + public void deleteDB() + { + deleteDatabase(false); + } + + public void deleteDatabase(boolean importPending) + { + String verificationCode = ConvertTo.toBase58(RandomCreations.generateRandomMixedCaseString(4, true), true, BASE58Type.UPPER); + + TextInputDialog dialog = new TextInputDialog(); + dialog.setTitle("Datenbank löschen"); + dialog.setHeaderText("Soll die Datenbank wirklich gelöscht werden?"); + dialog.setContentText("Zur Bestätigung gib folgenden Code ein:\t" + verificationCode); + Stage dialogStage = (Stage)dialog.getDialogPane().getScene().getWindow(); + dialogStage.getIcons().add(controller.getIcon()); + dialogStage.initOwner(controller.getStage()); + + Optional<String> result = dialog.showAndWait(); + if(result.isPresent()) + { + if(result.get().equals(verificationCode)) + { + Stage modalStage = showModal("Vorgang läuft", "Die Datenbank wird gelöscht, bitte warten..."); + + Worker.runLater(() -> { try { - Utils.saveSettings(controller.getSettings()); + ServerConnection connection = new ServerConnection(controller.getSettings()); + connection.deleteDatabase(); + Platform.runLater(() -> { + if(modalStage != null) + { + modalStage.close(); + if(importPending) + { + importDatabase(); + } + } + }); } - catch(IOException e) + catch(Exception e) { Logger.error(e); - AlertGenerator.showAlert(AlertType.ERROR, "Fehler", "", "Beim Speichern der Einstellungen ist ein Fehler aufgetreten", controller.getIcon(), controller.getStage(), null, false); - } - controller.refresh(controller.getFilterSettings()); - controller.showNotification("Erfolgreich gespeichert"); - } - else - { - AlertGenerator.showAlert(AlertType.WARNING, "Warnung", "", "Bitte gib deine gewünschte Währung ein!", controller.getIcon(), controller.getStage(), null, false); - } + Platform.runLater(() -> { + if(modalStage != null) + { + modalStage.close(); + } + controller.showConnectionErrorAlert(ExceptionHandler.getMessageForException(e)); + }); + } + }); } else { - AlertGenerator.showAlert(AlertType.WARNING, "Warnung", "", "Das Server Passwortfeld darf nicht leer sein!", controller.getIcon(), controller.getStage(), null, false); + AlertGenerator.showAlert(AlertType.WARNING, "Warnung", "", "Die Eingabe stimmt nicht mit dem Bestätigungscode überein.", controller.getIcon(), controller.getStage(), null, false); + deleteDB(); } } - else + } + + private Stage showModal(String title, String message) + { + try { - AlertGenerator.showAlert(AlertType.WARNING, "Warnung", "", "Das Feld für die Server URL darf nicht leer sein!", controller.getIcon(), controller.getStage(), null, false); + FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/de/deadlocker8/budgetmaster/ui/Modal.fxml")); + Parent root = (Parent)fxmlLoader.load(); + Stage newStage = new Stage(); + newStage.initOwner(controller.getStage()); + newStage.initModality(Modality.APPLICATION_MODAL); + newStage.setTitle(title); + newStage.setScene(new Scene(root)); + newStage.getIcons().add(controller.getIcon()); + newStage.setResizable(false); + ModalController newController = fxmlLoader.getController(); + newController.init(newStage, message); + newStage.show(); + + return newStage; + } + catch(IOException e) + { + Logger.error(e); + return null; } } } \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/ui/SettingsTab.fxml b/src/de/deadlocker8/budgetmaster/ui/SettingsTab.fxml index 0fc94536e52887e64a3a83aaab44298d89253d10..8014a8cab8429d648976e326a52d09a1abbe935d 100644 --- a/src/de/deadlocker8/budgetmaster/ui/SettingsTab.fxml +++ b/src/de/deadlocker8/budgetmaster/ui/SettingsTab.fxml @@ -4,9 +4,11 @@ <?import javafx.scene.control.Button?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.RadioButton?> +<?import javafx.scene.control.TextArea?> <?import javafx.scene.control.TextField?> <?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.Region?> <?import javafx.scene.layout.VBox?> <?import javafx.scene.text.Font?> @@ -14,7 +16,7 @@ <children> <VBox alignment="TOP_CENTER" layoutY="24.0" prefHeight="562.0" prefWidth="772.0" spacing="25.0" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="25.0"> <children> - <HBox prefHeight="70.0"> + <HBox prefHeight="359.0" prefWidth="722.0"> <children> <VBox alignment="CENTER_RIGHT" prefHeight="25.0" prefWidth="158.0" spacing="20.0"> <children> @@ -41,6 +43,39 @@ <Font name="System Bold" size="16.0" /> </font> </Label> + <Label fx:id="labelSecret111" alignment="CENTER_RIGHT" contentDisplay="RIGHT" maxHeight="-Infinity" minHeight="60.0" prefWidth="158.0" text="Vertrauenswürdige Hosts:" textAlignment="RIGHT" wrapText="true"> + <font> + <Font name="System Bold" size="16.0" /> + </font> + <VBox.margin> + <Insets top="-5.0" /> + </VBox.margin> + </Label> + <Label fx:id="labelSecret1111" alignment="CENTER" contentDisplay="CENTER" maxHeight="-Infinity" text="(ein Host pro Zeile)" textAlignment="CENTER" wrapText="true" VBox.vgrow="ALWAYS"> + <font> + <Font size="14.0" /> + </font> + <VBox.margin> + <Insets top="-20.0" /> + </VBox.margin> + </Label> + <Label fx:id="labelSecret1112" alignment="CENTER_RIGHT" contentDisplay="RIGHT" maxHeight="-Infinity" prefWidth="158.0" text="Datenbank:" textAlignment="RIGHT" wrapText="true"> + <font> + <Font name="System Bold" size="16.0" /> + </font> + <VBox.margin> + <Insets top="35.0" /> + </VBox.margin> + </Label> + <Label fx:id="labelSecret11121" alignment="CENTER_RIGHT" contentDisplay="RIGHT" maxHeight="-Infinity" prefWidth="158.0" textAlignment="RIGHT" wrapText="true"> + <font> + <Font name="System Bold" size="16.0" /> + </font> + <VBox.margin> + <Insets top="15.0" /> + </VBox.margin> + </Label> + <Region prefHeight="19.0" prefWidth="158.0" VBox.vgrow="ALWAYS" /> </children> <HBox.margin> <Insets right="25.0" /> @@ -51,14 +86,14 @@ <TextField fx:id="textFieldURL" /> <TextField fx:id="textFieldSecret" /> <TextField fx:id="textFieldCurrency" /> - <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0"> + <HBox alignment="CENTER" prefHeight="11.0" prefWidth="539.0" spacing="30.0"> <children> <RadioButton fx:id="radioButtonRestActivated" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="aktiviert"> <font> <Font size="14.0" /> </font> <HBox.margin> - <Insets right="30.0" /> + <Insets /> </HBox.margin> </RadioButton> <RadioButton fx:id="radioButtonRestDeactivated" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="deaktiviert" HBox.hgrow="ALWAYS"> @@ -68,15 +103,45 @@ </RadioButton> </children> </HBox> + <TextArea fx:id="textAreaTrustedHosts" minHeight="110.0" prefHeight="148.0" prefWidth="539.0" VBox.vgrow="ALWAYS"> + <VBox.margin> + <Insets /> + </VBox.margin></TextArea> + <HBox alignment="CENTER_LEFT" prefHeight="11.0" prefWidth="539.0" spacing="30.0"> + <children> + <Button fx:id="buttonExportDB" mnemonicParsing="false" onAction="#exportDB" text="Exportieren"> + <font> + <Font name="System Bold" size="14.0" /> + </font> + </Button> + <Button fx:id="buttonImportDB" mnemonicParsing="false" onAction="#importDB" text="Importieren"> + <font> + <Font name="System Bold" size="14.0" /> + </font> + </Button> + <Button fx:id="buttonDeleteDB" mnemonicParsing="false" onAction="#deleteDB" text="Löschen"> + <font> + <Font name="System Bold" size="14.0" /> + </font> + </Button> + </children> + <VBox.margin> + <Insets /> + </VBox.margin> + </HBox> + <Button fx:id="buttonSave" mnemonicParsing="false" onAction="#save" text="Speichern"> + <font> + <Font name="System Bold" size="14.0" /> + </font> + <VBox.margin> + <Insets top="15.0" /> + </VBox.margin> + </Button> + <Region prefWidth="158.0" VBox.vgrow="ALWAYS" /> </children> </VBox> </children> </HBox> - <Button fx:id="buttonSave" mnemonicParsing="false" onAction="#save" text="Speichern"> - <font> - <Font name="System Bold" size="14.0" /> - </font> - </Button> </children> <padding> <Insets right="50.0" /> diff --git a/src/de/deadlocker8/budgetmaster/ui/cells/PaymentCell.java b/src/de/deadlocker8/budgetmaster/ui/cells/PaymentCell.java index 8a6b957911d7bf78d4bb359eb197b4e8c737f39b..e096c9a72a0c14fdabd8972136b64e235e739ac2 100644 --- a/src/de/deadlocker8/budgetmaster/ui/cells/PaymentCell.java +++ b/src/de/deadlocker8/budgetmaster/ui/cells/PaymentCell.java @@ -69,6 +69,7 @@ public class PaymentCell extends ListCell<Payment> labelDate.setAlignment(Pos.CENTER); labelDate.getStyleClass().add("greylabel"); labelDate.setStyle("-fx-font-weight: bold; -fx-font-size: 16; -fx-text-fill: #212121"); + labelDate.setMinWidth(75); hbox.getChildren().add(labelDate); FontIcon iconRepeating = new FontIcon(FontIconType.CALENDAR); @@ -88,7 +89,7 @@ public class PaymentCell extends ListCell<Payment> labelRepeating.setAlignment(Pos.CENTER); labelRepeating.getStyleClass().add("greylabel"); hbox.getChildren().add(labelRepeating); - HBox.setMargin(labelRepeating, new Insets(0, 30, 0, 15)); + HBox.setMargin(labelRepeating, new Insets(0, 20, 0, 15)); String categoryName = category.getName(); if(categoryName.equals("NONE")) @@ -97,8 +98,8 @@ public class PaymentCell extends ListCell<Payment> } Label labelCircle = new Label(categoryName.substring(0, 1).toUpperCase()); - labelCircle.setPrefWidth(HEIGHT); - labelCircle.setPrefHeight(HEIGHT); + labelCircle.setMinWidth(HEIGHT); + labelCircle.setMinHeight(HEIGHT); labelCircle.setAlignment(Pos.CENTER); labelCircle.getStyleClass().add("greylabel"); String textColor = ConvertTo.toRGBHex(ConvertTo.getAppropriateTextColor(category.getColor())); @@ -106,14 +107,13 @@ public class PaymentCell extends ListCell<Payment> Tooltip tooltip = new Tooltip(categoryName); tooltip.setStyle("-fx-font-size: 14"); labelCircle.setTooltip(tooltip); - hbox.getChildren().add(labelCircle); - + hbox.getChildren().add(labelCircle); Label labelName = new Label(item.getName()); labelName.setPrefHeight(HEIGHT); labelName.setStyle("-fx-font-weight: bold; -fx-font-size: 16; -fx-text-fill: #212121"); labelName.setAlignment(Pos.CENTER); - labelName.getStyleClass().add("greylabel"); + labelName.getStyleClass().add("greylabel"); hbox.getChildren().add(labelName); HBox.setMargin(labelName, new Insets(0, 0, 0, 20)); @@ -126,6 +126,7 @@ public class PaymentCell extends ListCell<Payment> labelBudget.setStyle("-fx-font-weight: bold; -fx-font-size: 16; -fx-text-fill: #247A2D"); labelBudget.setAlignment(Pos.CENTER); labelBudget.getStyleClass().add("greylabel"); + labelBudget.setMinWidth(90); hbox.getChildren().add(labelBudget); HBox.setMargin(labelBudget, new Insets(0, 0, 0, 20)); @@ -150,17 +151,17 @@ public class PaymentCell extends ListCell<Payment> Alert alert = new Alert(Alert.AlertType.CONFIRMATION); alert.setTitle("Zahlung löschen"); alert.setHeaderText(""); - alert.setContentText("Diese Zahlung wirklich unwiederruflich löschen?"); + alert.setContentText("Diese Zahlung wirklich unwiderruflich löschen?"); Stage dialogStage = (Stage)alert.getDialogPane().getScene().getWindow(); dialogStage.getIcons().add(paymentController.getController().getIcon()); dialogStage.centerOnScreen(); if(item instanceof RepeatingPaymentEntry) { - alert.setContentText("Es handelt sich um eine wiederkehrende Zahlung. Wie soll gelöscht werden?"); + alert.setContentText("Es handelt sich um eine wiederkehrende Zahlung. Welche Zahlungen sollen gelöscht werden?"); - ButtonType buttonTypeOne = new ButtonType("Komplett löschen"); - ButtonType buttonTypeTwo = new ButtonType("Alle zukünftigen Löschen"); + ButtonType buttonTypeOne = new ButtonType("Alle"); + ButtonType buttonTypeTwo = new ButtonType("Alle zukünftigen"); ButtonType buttonTypeCancel = new ButtonType("Abbrechen", ButtonData.CANCEL_CLOSE); alert.getButtonTypes().setAll(buttonTypeOne, buttonTypeTwo, buttonTypeCancel); @@ -185,14 +186,14 @@ public class PaymentCell extends ListCell<Payment> } }); hbox.getChildren().add(buttonDelete); - HBox.setMargin(buttonDelete, new Insets(0, 0, 0, 25)); + HBox.setMargin(buttonDelete, new Insets(0, 0, 0, 10)); // don't allow "Übertrag" to be deleted if(item.getID() == -1) { buttonDelete.setVisible(false); } - hbox.setPadding(new Insets(10)); + hbox.setPadding(new Insets(10, 8, 10, 5)); setStyle("-fx-background: transparent; -fx-border-color: #545454; -fx-border-width: 0 0 1 0"); setGraphic(hbox); setAlignment(Pos.CENTER); diff --git a/src/de/deadlocker8/budgetmaster/ui/colorPick/ColorPickGUI.fxml b/src/de/deadlocker8/budgetmaster/ui/colorPick/ColorPickGUI.fxml index c96c6768ced8598de0f3758b2a23d8c5133879c3..7b0f539542397085a05f4febd10d01875ca5c498 100644 --- a/src/de/deadlocker8/budgetmaster/ui/colorPick/ColorPickGUI.fxml +++ b/src/de/deadlocker8/budgetmaster/ui/colorPick/ColorPickGUI.fxml @@ -90,12 +90,12 @@ </HBox> <HBox alignment="BOTTOM_CENTER" prefHeight="0.0" prefWidth="299.0" spacing="25.0" VBox.vgrow="ALWAYS"> <children> - <Button fx:id="buttonSave" mnemonicParsing="false" onAction="#save" text="Speichern"> + <Button fx:id="buttonCancel" mnemonicParsing="false" onAction="#cancel" text="Abbrechen"> <font> <Font name="System Bold" size="14.0" /> </font> </Button> - <Button fx:id="buttonCancel" mnemonicParsing="false" onAction="#cancel" text="Abbrechen"> + <Button fx:id="buttonSave" mnemonicParsing="false" onAction="#save" text="Speichern"> <font> <Font name="System Bold" size="14.0" /> </font> diff --git a/src/de/deadlocker8/budgetmasterserver/logic/Database.java b/src/de/deadlocker8/budgetmasterserver/logic/Database.java new file mode 100644 index 0000000000000000000000000000000000000000..05740dae2752cab71a041f93dcfd08911e4d5088 --- /dev/null +++ b/src/de/deadlocker8/budgetmasterserver/logic/Database.java @@ -0,0 +1,41 @@ +package de.deadlocker8.budgetmasterserver.logic; + +import java.util.ArrayList; + +import de.deadlocker8.budgetmaster.logic.Category; +import de.deadlocker8.budgetmaster.logic.NormalPayment; +import de.deadlocker8.budgetmaster.logic.RepeatingPayment; + +public class Database +{ + private ArrayList<Category> categories; + private ArrayList<NormalPayment> normalPayments; + private ArrayList<RepeatingPayment> repeatingPayments; + + public Database() + { + + } + + public Database(ArrayList<Category> categories, ArrayList<NormalPayment> normalPayments, ArrayList<RepeatingPayment> repeatingPayments) + { + this.categories = categories; + this.normalPayments = normalPayments; + this.repeatingPayments = repeatingPayments; + } + + public ArrayList<Category> getCategories() + { + return categories; + } + + public ArrayList<NormalPayment> getNormalPayments() + { + return normalPayments; + } + + public ArrayList<RepeatingPayment> getRepeatingPayments() + { + return repeatingPayments; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/main/DatabaseCreator.java b/src/de/deadlocker8/budgetmasterserver/logic/DatabaseCreator.java similarity index 96% rename from src/de/deadlocker8/budgetmasterserver/main/DatabaseCreator.java rename to src/de/deadlocker8/budgetmasterserver/logic/DatabaseCreator.java index 7a512f12e66f69b9d9b4a4a98e7a0794ad959f34..398629d7b146d0aee5a4b3d3bbac5451b2b39f05 100644 --- a/src/de/deadlocker8/budgetmasterserver/main/DatabaseCreator.java +++ b/src/de/deadlocker8/budgetmasterserver/logic/DatabaseCreator.java @@ -1,4 +1,4 @@ -package de.deadlocker8.budgetmasterserver.main; +package de.deadlocker8.budgetmasterserver.logic; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -109,7 +109,7 @@ public class DatabaseCreator Statement stmt = null; String query = "CREATE TABLE `payment` (" + "`ID` int(11) NOT NULL COMMENT 'ID'," + - "`Name` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'payment name (description)'," + + "`Name` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'payment name (description)'," + "`CategoryID` int(11) DEFAULT NULL COMMENT 'category ID'," + "`Amount` int(11) DEFAULT NULL COMMENT 'amount in cents'," + "`Date` date DEFAULT NULL COMMENT 'payment date'," + @@ -190,7 +190,7 @@ public class DatabaseCreator Statement stmt = null; String query = "CREATE TABLE `repeating_payment` (" + "`ID` int(11) NOT NULL COMMENT 'ID'," + - "`Name` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'payment name (description)'," + + "`Name` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'payment name (description)'," + "`CategoryID` int(11) DEFAULT NULL COMMENT 'category ID'," + "`Amount` int(11) DEFAULT NULL COMMENT 'amount in cents'," + "`Date` date DEFAULT NULL COMMENT 'payment date'," + diff --git a/src/de/deadlocker8/budgetmasterserver/logic/DatabaseExporter.java b/src/de/deadlocker8/budgetmasterserver/logic/DatabaseExporter.java new file mode 100644 index 0000000000000000000000000000000000000000..eeda5e9593b00b74b71a76672598123af4467320 --- /dev/null +++ b/src/de/deadlocker8/budgetmasterserver/logic/DatabaseExporter.java @@ -0,0 +1,171 @@ +package de.deadlocker8.budgetmasterserver.logic; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; + +import de.deadlocker8.budgetmaster.logic.Category; +import de.deadlocker8.budgetmaster.logic.NormalPayment; +import de.deadlocker8.budgetmaster.logic.RepeatingPayment; +import javafx.scene.paint.Color; +import logger.Logger; + +public class DatabaseExporter +{ + private Connection connection; + + public DatabaseExporter(Settings settings) throws IllegalStateException + { + try + { + this.connection = DriverManager.getConnection(settings.getDatabaseUrl() + settings.getDatabaseName() + "?useLegacyDatetimeCode=false&serverTimezone=Europe/Berlin", settings.getDatabaseUsername(), settings.getDatabasePassword()); + } + catch(Exception e) + { + Logger.error(e); + throw new IllegalStateException("Cannot connect the database!", e); + } + } + + public Database exportDatabase() + { + ArrayList<Category> categories = getAllCategories(); + ArrayList<NormalPayment> normalPayments = getAllNormalPayments(); + ArrayList<RepeatingPayment> repeatingPayments = getAllRepeatingPayments(); + + return new Database(categories, normalPayments, repeatingPayments); + } + + private ArrayList<Category> getAllCategories() + { + Statement stmt = null; + String query = "SELECT * FROM category ORDER BY category.ID"; + ArrayList<Category> results = new ArrayList<>(); + try + { + stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery(query); + while(rs.next()) + { + int id = rs.getInt("ID"); + String name = rs.getString("Name"); + String color = rs.getString("Color"); + + results.add(new Category(id, name, Color.web(color))); + } + } + catch(SQLException e) + { + Logger.error(e); + } + finally + { + if(stmt != null) + { + try + { + stmt.close(); + } + catch(SQLException e) + { + } + } + } + + return results; + } + + private ArrayList<NormalPayment> getAllNormalPayments() + { + Statement stmt = null; + String query = "SELECT * FROM payment;"; + + ArrayList<NormalPayment> results = new ArrayList<>(); + try + { + stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery(query); + + while(rs.next()) + { + int resultID = rs.getInt("ID"); + String name = rs.getString("Name"); + int amount = rs.getInt("amount"); + String date = rs.getString("Date"); + int categoryID = rs.getInt("CategoryID"); + String description = rs.getString("Description"); + + results.add(new NormalPayment(resultID, amount, date, categoryID, name, description)); + } + } + catch(SQLException e) + { + Logger.error(e); + } + finally + { + if(stmt != null) + { + try + { + stmt.close(); + } + catch(SQLException e) + { + } + } + } + + return results; + } + + private ArrayList<RepeatingPayment> getAllRepeatingPayments() + { + Statement stmt = null; + String query = "SELECT * FROM repeating_payment;"; + + ArrayList<RepeatingPayment> results = new ArrayList<>(); + try + { + stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery(query); + + while(rs.next()) + { + int resultID = rs.getInt("ID"); + String name = rs.getString("Name"); + String description = rs.getString("Description"); + int amount = rs.getInt("amount"); + String date = rs.getString("Date"); + int categoryID = rs.getInt("CategoryID"); + int repeatInterval = rs.getInt("RepeatInterval"); + String repeatEndDate = rs.getString("RepeatEndDate"); + int repeatMonthDay = rs.getInt("RepeatMonthDay"); + + results.add(new RepeatingPayment(resultID, amount, date, categoryID, name, description, repeatInterval, repeatEndDate, repeatMonthDay)); + } + } + catch(SQLException e) + { + Logger.error(e); + } + finally + { + if(stmt != null) + { + try + { + stmt.close(); + } + catch(SQLException e) + { + } + } + } + + return results; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/main/DatabaseHandler.java b/src/de/deadlocker8/budgetmasterserver/logic/DatabaseHandler.java similarity index 62% rename from src/de/deadlocker8/budgetmasterserver/main/DatabaseHandler.java rename to src/de/deadlocker8/budgetmasterserver/logic/DatabaseHandler.java index 61b8ee0401aa153b527c1fd2b34190075e333a88..328d6ee8226b84f57f3d758d0b2c929262960b51 100644 --- a/src/de/deadlocker8/budgetmasterserver/main/DatabaseHandler.java +++ b/src/de/deadlocker8/budgetmasterserver/logic/DatabaseHandler.java @@ -1,4 +1,4 @@ -package de.deadlocker8.budgetmasterserver.main; +package de.deadlocker8.budgetmasterserver.logic; import java.sql.Connection; import java.sql.DriverManager; @@ -30,7 +30,7 @@ public class DatabaseHandler { try { - this.connection = DriverManager.getConnection(settings.getDatabaseUrl() + settings.getDatabaseName() + "?useLegacyDatetimeCode=false&serverTimezone=Europe/Berlin", settings.getDatabaseUsername(), settings.getDatabasePassword()); + this.connection = DriverManager.getConnection(settings.getDatabaseUrl() + settings.getDatabaseName() + "?useLegacyDatetimeCode=false&serverTimezone=Europe/Berlin&autoReconnect=true&wait_timeout=86400", settings.getDatabaseUsername(), settings.getDatabasePassword()); new DatabaseCreator(connection, settings); Logger.info("Successfully initialized database (" + settings.getDatabaseUrl() + settings.getDatabaseName() + ")"); } @@ -40,15 +40,29 @@ public class DatabaseHandler throw new IllegalStateException("Cannot connect the database!", e); } } + + private void closeConnection(Statement statement) + { + if(statement != null) + { + try + { + statement.close(); + } + catch(SQLException e) + { + } + } + } /* * GET */ - public DateTime getFirstNormalPaymentDate() + public int getLastInsertID() { Statement stmt = null; - String query = "SELECT MIN(Date) as \"min\" FROM payment"; - DateTime dateTime = null; + String query = "SELECT LAST_INSERT_ID();"; + int lastInsertID = 0; try { stmt = connection.createStatement(); @@ -56,7 +70,7 @@ public class DatabaseHandler while(rs.next()) { - dateTime = formatter.parseDateTime(rs.getString("min")); + lastInsertID = rs.getInt("LAST_INSERT_ID()"); } } catch(SQLException e) @@ -65,21 +79,47 @@ public class DatabaseHandler } finally { - if(stmt != null) + closeConnection(stmt); + } + + return lastInsertID; + } + + public DateTime getFirstNormalPaymentDate() + { + Statement stmt = null; + String query = "SELECT MIN(Date) as \"min\" FROM payment"; + DateTime dateTime = null; + try + { + stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery(query); + + while(rs.next()) { - try + String min = rs.getString("min"); + if(min == null) { - stmt.close(); + dateTime = null; } - catch(SQLException e) + else { + dateTime = formatter.parseDateTime(rs.getString("min")); } } } + catch(SQLException e) + { + Logger.error(e); + } + finally + { + closeConnection(stmt); + } return dateTime; } - + public DateTime getFirstRepeatingPaymentDate() { Statement stmt = null; @@ -92,7 +132,15 @@ public class DatabaseHandler while(rs.next()) { - dateTime = formatter.parseDateTime(rs.getString("min")); + String min = rs.getString("min"); + if(min == null) + { + dateTime = null; + } + else + { + dateTime = formatter.parseDateTime(rs.getString("min")); + } } } catch(SQLException e) @@ -101,36 +149,35 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } return dateTime; } public int getRestForAllPreviousMonths(int year, int month) - { + { + DateTimeFormatter formatter = DateTimeFormat.forPattern("MM.yyyy"); + String dateString = String.valueOf(month) + "." + year; + DateTime currentDate = formatter.parseDateTime(dateString); + DateTime firstNormalPaymentDate = getFirstNormalPaymentDate(); + if(firstNormalPaymentDate == null) + { + firstNormalPaymentDate = currentDate; + } DateTime firstRepeatingPaymentDate = getFirstRepeatingPaymentDate(); - + if(firstRepeatingPaymentDate == null) + { + firstRepeatingPaymentDate = currentDate; + } + DateTime firstDate = firstNormalPaymentDate; if(firstRepeatingPaymentDate.isBefore(firstNormalPaymentDate)) { firstDate = firstRepeatingPaymentDate; } - - DateTimeFormatter formatter = DateTimeFormat.forPattern("MM.yyyy"); - String dateString = String.valueOf(month) + "." + year; - DateTime currentDate = formatter.parseDateTime(dateString);// - + if(firstDate.isAfter(currentDate)) { return 0; @@ -139,10 +186,10 @@ public class DatabaseHandler int startYear = firstDate.getYear(); int startMonth = firstDate.getMonthOfYear(); int totalRest = 0; - + while(startYear < year || startMonth < month) { - totalRest += getRest(startYear, startMonth); + totalRest += getRest(startYear, startMonth); startMonth++; if(startMonth > 12) @@ -159,13 +206,13 @@ public class DatabaseHandler ArrayList<Payment> payments = new ArrayList<>(); payments.addAll(getPayments(year, month)); payments.addAll(getRepeatingPayments(year, month)); - + int rest = 0; for(Payment currentPayment : payments) { rest += currentPayment.getAmount(); } - + return rest; } @@ -193,30 +240,21 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } return results; } - + public Category getCategory(int ID) { Statement stmt = null; - String query = "SELECT * FROM category WHERE category.ID = " + ID; + String query = "SELECT * FROM category WHERE category.ID = " + ID; Category result = null; try { stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery(query); + ResultSet rs = stmt.executeQuery(query); while(rs.next()) { int id = rs.getInt("ID"); @@ -232,21 +270,71 @@ public class DatabaseHandler } finally { - if(stmt != null) + closeConnection(stmt); + } + + return result; + } + + public Category getCategory(String name, Color color) + { + Statement stmt = null; + String query = "SELECT * FROM category WHERE category.name = \"" + name + "\" AND category.color = \"" + ConvertTo.toRGBHexWithoutOpacity(color) + "\";"; + Category result = null; + try + { + stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery(query); + while(rs.next()) + { + int id = rs.getInt("ID"); + String categoryName = rs.getString("Name"); + String categoryColor = rs.getString("Color"); + + result = new Category(id, categoryName, Color.web(categoryColor)); + } + } + catch(SQLException e) + { + Logger.error(e); + } + finally + { + closeConnection(stmt); + } + + return result; + } + + public boolean categoryExists(int ID) + { + Statement stmt = null; + String query = "SELECT COUNT(ID) as \"count\" FROM category WHERE category.ID = " + ID; + boolean exists = false; + try + { + stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery(query); + while(rs.next()) { - try - { - stmt.close(); - } - catch(SQLException e) + if(rs.getInt("count") > 0) { + exists = true; } } } + catch(SQLException e) + { + Logger.error(e); + } + finally + { + closeConnection(stmt); + } - return result; + return exists; } - + public NormalPayment getPayment(int ID) { Statement stmt = null; @@ -274,16 +362,7 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } return null; @@ -294,6 +373,41 @@ public class DatabaseHandler Statement stmt = null; String query = "SELECT * FROM payment WHERE YEAR(Date) = " + year + " AND MONTH(Date) = " + month; + ArrayList<NormalPayment> results = new ArrayList<>(); + try + { + stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery(query); + + while(rs.next()) + { + int resultID = rs.getInt("ID"); + String name = rs.getString("Name"); + int amount = rs.getInt("amount"); + String date = rs.getString("Date"); + int categoryID = rs.getInt("CategoryID"); + String description = rs.getString("Description"); + + results.add(new NormalPayment(resultID, amount, date, categoryID, name, description)); + } + } + catch(SQLException e) + { + Logger.error(e); + } + finally + { + closeConnection(stmt); + } + + return results; + } + + public ArrayList<NormalPayment> getPaymentsBetween(String startDate, String endDate) + { + Statement stmt = null; + String query = "SELECT * FROM payment WHERE DATE(Date) BETWEEN '" + startDate + "' AND '" + endDate + "';"; + ArrayList<NormalPayment> results = new ArrayList<>(); try { @@ -318,16 +432,7 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } return results; @@ -336,7 +441,47 @@ public class DatabaseHandler public ArrayList<RepeatingPaymentEntry> getRepeatingPayments(int year, int month) { Statement stmt = null; - String query = "SELECT repeating_entry.ID, repeating_entry.RepeatingPaymentID, repeating_entry.Date, repeating_payment.Name, repeating_payment.CategoryID, repeating_payment.Amount, repeating_payment.RepeatInterval, repeating_payment.RepeatEndDate, repeating_payment.RepeatMonthDay, repeating_payment.Description FROM repeating_entry, repeating_payment WHERE repeating_entry.RepeatingPaymentID = repeating_payment.ID AND YEAR(repeating_entry.Date) = " + year + " AND MONTH(repeating_entry.Date) = " + month; + String query = "SELECT repeating_entry.ID, repeating_entry.RepeatingPaymentID, repeating_entry.Date, repeating_payment.Name, repeating_payment.CategoryID, repeating_payment.Amount, repeating_payment.RepeatInterval, repeating_payment.RepeatEndDate, repeating_payment.RepeatMonthDay, repeating_payment.Description FROM repeating_entry, repeating_payment WHERE repeating_entry.RepeatingPaymentID = repeating_payment.ID AND YEAR(repeating_entry.Date) = " + + year + " AND MONTH(repeating_entry.Date) = " + month; + + ArrayList<RepeatingPaymentEntry> results = new ArrayList<>(); + try + { + stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery(query); + + while(rs.next()) + { + int resultID = rs.getInt("ID"); + int repeatingPaymentID = rs.getInt("repeatingPaymentID"); + String name = rs.getString("Name"); + String description = rs.getString("Description"); + int amount = rs.getInt("amount"); + String date = rs.getString("Date"); + int categoryID = rs.getInt("CategoryID"); + int repeatInterval = rs.getInt("RepeatInterval"); + String repeatEndDate = rs.getString("RepeatEndDate"); + int repeatMonthDay = rs.getInt("RepeatMonthDay"); + + results.add(new RepeatingPaymentEntry(resultID, repeatingPaymentID, date, amount, categoryID, name, description, repeatInterval, repeatEndDate, repeatMonthDay)); + } + } + catch(SQLException e) + { + Logger.error(e); + } + finally + { + closeConnection(stmt); + } + + return results; + } + + public ArrayList<RepeatingPaymentEntry> getRepeatingPaymentsBetween(String startDate, String endDate) + { + Statement stmt = null; + String query = "SELECT repeating_entry.ID, repeating_entry.RepeatingPaymentID, repeating_entry.Date, repeating_payment.Name, repeating_payment.CategoryID, repeating_payment.Amount, repeating_payment.RepeatInterval, repeating_payment.RepeatEndDate, repeating_payment.RepeatMonthDay, repeating_payment.Description FROM repeating_entry, repeating_payment WHERE repeating_entry.RepeatingPaymentID = repeating_payment.ID AND DATE(repeating_entry.Date) BETWEEN '" + startDate + "' AND '" + endDate + "';"; ArrayList<RepeatingPaymentEntry> results = new ArrayList<>(); try @@ -366,23 +511,14 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } return results; } - + public ArrayList<RepeatingPayment> getAllRepeatingPayments() - { + { Statement stmt = null; String query = "SELECT * FROM repeating_payment;"; @@ -394,15 +530,15 @@ public class DatabaseHandler while(rs.next()) { - int resultID = rs.getInt("ID"); + int resultID = rs.getInt("ID"); String name = rs.getString("Name"); int amount = rs.getInt("amount"); - String date = rs.getString("Date"); + String date = rs.getString("Date"); String description = rs.getString("Description"); int categoryID = rs.getInt("CategoryID"); int repeatInterval = rs.getInt("RepeatInterval"); String repeatEndDate = rs.getString("RepeatEndDate"); - int repeatMonthDay = rs.getInt("RepeatMonthDay"); + int repeatMonthDay = rs.getInt("RepeatMonthDay"); results.add(new RepeatingPayment(resultID, amount, date, categoryID, name, description, repeatInterval, repeatEndDate, repeatMonthDay)); } @@ -413,21 +549,12 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } return results; } - + public ArrayList<LatestRepeatingPayment> getLatestRepeatingPaymentEntries() { Statement stmt = null; @@ -442,9 +569,9 @@ public class DatabaseHandler while(rs.next()) { int resultID = rs.getInt("ID"); - int repeatingPaymentID = rs.getInt("repeatingPaymentID"); + int repeatingPaymentID = rs.getInt("repeatingPaymentID"); String date = rs.getString("LastDate"); - + results.add(new LatestRepeatingPayment(resultID, repeatingPaymentID, date)); } } @@ -454,33 +581,24 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } return results; } - + public RepeatingPayment getRepeatingPayment(int ID) { Statement stmt = null; - String query = "SELECT * FROM repeating_payment WHERE ID = " + ID; + String query = "SELECT * FROM repeating_payment WHERE ID = " + ID; RepeatingPayment result = null; try { stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery(query); + ResultSet rs = stmt.executeQuery(query); while(rs.next()) { - int id = rs.getInt("ID"); + int id = rs.getInt("ID"); int amount = rs.getInt("amount"); String date = rs.getString("Date"); int categoryID = rs.getInt("CategoryID"); @@ -499,16 +617,7 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } return result; @@ -532,19 +641,10 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } } - + public void deletePayment(int ID) { Statement stmt = null; @@ -560,19 +660,10 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } } - + public void deleteRepeatingPayment(int ID) { Statement stmt = null; @@ -588,16 +679,38 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); + } + } + + public void deleteDatabase() + { + Statement stmt = null; + String tableCategory = "DROP TABLE IF EXISTS category;"; + String tablePayment = "DROP TABLE IF EXISTS payment;"; + String tableRepeatingPayment = "DROP TABLE IF EXISTS repeating_payment;"; + String tableRepeatingEntry = "DROP TABLE IF EXISTS repeating_entry;"; + try + { + stmt = connection.createStatement(); + stmt.execute("SET FOREIGN_KEY_CHECKS = 0;"); + stmt.execute(tableCategory); + Logger.info("Deleted table: category"); + stmt.execute(tablePayment); + Logger.info("Deleted table: payment"); + stmt.execute(tableRepeatingPayment); + Logger.info("Deleted table: repeating_payment"); + stmt.execute(tableRepeatingEntry); + Logger.info("Deleted table: repeating_entry"); + stmt.execute("SET FOREIGN_KEY_CHECKS = 1;"); + } + catch(SQLException e) + { + Logger.error(e); + } + finally + { + closeConnection(stmt); } } @@ -619,16 +732,26 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); + } + } + + public void importCategory(Category category) + { + Statement stmt = null; + String query = "INSERT INTO category (ID, Name, Color) VALUES('" + category.getID() + "', '" + category.getName() + "' , '" + ConvertTo.toRGBHexWithoutOpacity(category.getColor()) + "');"; + try + { + stmt = connection.createStatement(); + stmt.execute(query); + } + catch(SQLException e) + { + Logger.error(e); + } + finally + { + closeConnection(stmt); } } @@ -647,33 +770,27 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } } - + public void addRepeatingPayment(int amount, String date, int categoryID, String name, String description, int repeatInterval, String repeatEndDate, int repeatMonthDay) { Statement stmt = null; - String query; - //A is placeholder for empty repeatEndDate - if(repeatEndDate.equals("A") || repeatEndDate == null) - { - query = "INSERT INTO repeating_payment (Amount, Date, CategoryID, Name, RepeatInterval, RepeatEndDate, RepeatMonthDay, Description) VALUES('" + amount + "' , '" + date + "' , '" + categoryID + "' , '" + name + "' , '" + repeatInterval + "' , NULL , '" + repeatMonthDay + "' , '" + description + "');"; + String query; + String correctRepeatEndDate = repeatEndDate; + if(correctRepeatEndDate == null || correctRepeatEndDate.equals("A")) + { + correctRepeatEndDate = "NULL"; } else { - query = "INSERT INTO repeating_payment (Amount, Date, CategoryID, Name, RepeatInterval, RepeatEndDate, RepeatMonthDay, Description) VALUES('" + amount + "' , '" + date + "' , '" + categoryID + "' , '" + name + "' , '" + repeatInterval + "' , '" + repeatEndDate + "' , '" + repeatMonthDay + "' , '" + description + "');"; + correctRepeatEndDate = "'" + correctRepeatEndDate + "'"; } - + + query = "INSERT INTO repeating_payment (Amount, Date, CategoryID, Name, RepeatInterval, RepeatEndDate, RepeatMonthDay, Description) VALUES('" + amount + "' , '" + date + "' , '" + categoryID + "' , '" + name + "' , '" + repeatInterval + "' , " + correctRepeatEndDate + " , '" + repeatMonthDay + + "' , '" + description + "');"; + try { stmt = connection.createStatement(); @@ -685,24 +802,15 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } } public void addRepeatingPaymentEntry(int repeatingPaymentID, String date) { Statement stmt = null; - String query; - query = "INSERT INTO repeating_entry (RepeatingPaymentID, Date) VALUES('" + repeatingPaymentID + "' , '" + date + "');"; + String query; + query = "INSERT INTO repeating_entry (RepeatingPaymentID, Date) VALUES('" + repeatingPaymentID + "' , '" + date + "');"; try { stmt = connection.createStatement(); @@ -714,16 +822,7 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } } @@ -745,19 +844,10 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } - } - + } + public void updateNormalPayment(int ID, int amount, String date, int categoryID, String name, String description) { Statement stmt = null; @@ -773,16 +863,7 @@ public class DatabaseHandler } finally { - if(stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - } - } + closeConnection(stmt); } } } \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/logic/DatabaseImporter.java b/src/de/deadlocker8/budgetmasterserver/logic/DatabaseImporter.java new file mode 100644 index 0000000000000000000000000000000000000000..7880c9c2dd7d0e1c3e61989d371114f79458b713 --- /dev/null +++ b/src/de/deadlocker8/budgetmasterserver/logic/DatabaseImporter.java @@ -0,0 +1,107 @@ +package de.deadlocker8.budgetmasterserver.logic; + +import java.util.ArrayList; +import java.util.Iterator; + +import de.deadlocker8.budgetmaster.logic.Category; +import de.deadlocker8.budgetmaster.logic.NormalPayment; +import de.deadlocker8.budgetmaster.logic.RepeatingPayment; + +public class DatabaseImporter +{ + private DatabaseHandler handler; + private ArrayList<Category> categories; + private ArrayList<NormalPayment> normalPayments; + private ArrayList<RepeatingPayment> repeatingPayments; + private ArrayList<NormalPayment> changedNormalPayments; + private ArrayList<RepeatingPayment> changedRepeatingPayments; + + public DatabaseImporter(DatabaseHandler handler) throws IllegalStateException + { + this.handler = handler; + } + + public void importDatabase(Database database) + { + this.categories = database.getCategories(); + this.normalPayments = database.getNormalPayments(); + this.repeatingPayments = database.getRepeatingPayments(); + this.changedNormalPayments = new ArrayList<>(); + this.changedRepeatingPayments = new ArrayList<>(); + + importAll(); + } + + private void importAll() + { + for(Category currentCategory : categories) + { + Category existingCategory = handler.getCategory(currentCategory.getName(), currentCategory.getColor()); + if(existingCategory == null) + { + handler.addCategory(currentCategory.getName(), currentCategory.getColor()); + int newID = handler.getLastInsertID(); + + updatePayments(currentCategory.getID(), newID); + } + else + { + updatePayments(currentCategory.getID(), existingCategory.getID()); + } + } + + //merge changed and remaining payments + normalPayments.addAll(changedNormalPayments); + repeatingPayments.addAll(changedRepeatingPayments); + + importNormalPayments(normalPayments); + importRepeatingPayments(repeatingPayments); + } + + private void updatePayments(int oldID, int newID) + { + //check normal payments for old category ID + Iterator<NormalPayment> iterator = normalPayments.iterator(); + while(iterator.hasNext()) + { + NormalPayment currentPayment = iterator.next(); + if(currentPayment.getCategoryID() == oldID) + { + currentPayment.setCategoryID(newID); + //remove payment from list to avoid overriding category ID again in the future + changedNormalPayments.add(currentPayment); + iterator.remove(); + } + } + + //check repeating payments for old category ID + Iterator<RepeatingPayment> iterator2 = repeatingPayments.iterator(); + while(iterator2.hasNext()) + { + RepeatingPayment currentPayment = iterator2.next(); + if(currentPayment.getCategoryID() == oldID) + { + currentPayment.setCategoryID(newID); + //remove payment from list to avoid overriding category ID again in the future + changedRepeatingPayments.add(currentPayment); + iterator2.remove(); + } + } + } + + private void importNormalPayments(ArrayList<NormalPayment> normalPayments) + { + for(NormalPayment currentPayment : normalPayments) + { + handler.addNormalPayment(currentPayment.getAmount(), currentPayment.getDate(), currentPayment.getCategoryID(), currentPayment.getName(), currentPayment.getDescription()); + } + } + + private void importRepeatingPayments(ArrayList<RepeatingPayment> repeatingPayments) + { + for(RepeatingPayment currentPayment : repeatingPayments) + { + handler.addRepeatingPayment(currentPayment.getAmount(), currentPayment.getDate(), currentPayment.getCategoryID(), currentPayment.getName(), currentPayment.getDescription(), currentPayment.getRepeatInterval(), currentPayment.getRepeatEndDate(), currentPayment.getRepeatMonthDay()); + } + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/main/Settings.java b/src/de/deadlocker8/budgetmasterserver/logic/Settings.java similarity index 96% rename from src/de/deadlocker8/budgetmasterserver/main/Settings.java rename to src/de/deadlocker8/budgetmasterserver/logic/Settings.java index 1dbc26be8a1cacc8e1ecedba6a2216ec604b3d8b..121ba938b3b45f8ab641e1b267e2b896a5777491 100644 --- a/src/de/deadlocker8/budgetmasterserver/main/Settings.java +++ b/src/de/deadlocker8/budgetmasterserver/logic/Settings.java @@ -1,4 +1,4 @@ -package de.deadlocker8.budgetmasterserver.main; +package de.deadlocker8.budgetmasterserver.logic; public class Settings { diff --git a/src/de/deadlocker8/budgetmasterserver/logic/Utils.java b/src/de/deadlocker8/budgetmasterserver/logic/Utils.java new file mode 100644 index 0000000000000000000000000000000000000000..265ab169df0cec23bb18cc16c6ebd9e87a9fa639 --- /dev/null +++ b/src/de/deadlocker8/budgetmasterserver/logic/Utils.java @@ -0,0 +1,23 @@ +package de.deadlocker8.budgetmasterserver.logic; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import com.google.gson.Gson; + +public class Utils +{ + public static Settings loadSettings() throws IOException, URISyntaxException + { + String settingsJSON; + Settings settings; + + Gson gson = new Gson(); + + settingsJSON = new String(Files.readAllBytes(Paths.get(Settings.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParent().resolve("settings.json"))); + settings = gson.fromJson(settingsJSON, Settings.class); + return settings; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/main/Main.java b/src/de/deadlocker8/budgetmasterserver/main/Main.java index 995f4e11b3ccfab77f7dd22b9c487ad83b414c3a..38a523850192a9d3ca547d8d454c82aa69c1ba55 100644 --- a/src/de/deadlocker8/budgetmasterserver/main/Main.java +++ b/src/de/deadlocker8/budgetmasterserver/main/Main.java @@ -4,11 +4,15 @@ import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.Locale; import java.util.ResourceBundle; +import de.deadlocker8.budgetmasterserver.logic.Settings; +import de.deadlocker8.budgetmasterserver.logic.Utils; import de.deadlocker8.budgetmasterserver.server.SparkServer; +import logger.FileOutputMode; import logger.LogLevel; import logger.Logger; @@ -22,35 +26,44 @@ public class Main Logger.appInfo(bundle.getString("app.name"), bundle.getString("version.name"), bundle.getString("version.code"), bundle.getString("version.date")); try { - File logFile = new File(Paths.get(SparkServer.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParent().toFile() + "/error.log"); - Logger.enableFileOutput(logFile); + File logFolder = Paths.get(SparkServer.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParent().toFile(); + Logger.enableFileOutput(logFolder, System.out, System.err, FileOutputMode.COMBINED); } catch(URISyntaxException e1) { Logger.error(e1); - } + } - if(!Files.exists(Paths.get("settings.json"))) + try { + Path settingsPath = Paths.get(Settings.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParent().resolve("settings.json"); + + if(!Files.exists(settingsPath)) + { + try + { + Files.copy(SparkServer.class.getClassLoader().getResourceAsStream("de/deadlocker8/budgetmasterserver/resources/settings.json"), settingsPath); + } + catch(IOException e) + { + Logger.error(e); + } + } + + Settings settings; try { - Files.copy(SparkServer.class.getClassLoader().getResourceAsStream("de/deadlocker8/budgetmasterserver/resources/settings.json"), Paths.get("settings.json")); + settings = Utils.loadSettings(); + new SparkServer(settings); } - catch(IOException e) + catch(IOException | URISyntaxException e) { Logger.error(e); } } - - Settings settings; - try - { - settings = Utils.loadSettings(); - new SparkServer(settings); - } - catch(IOException e) - { - Logger.error(e); - } + catch(URISyntaxException e1) + { + Logger.error(e1); + } } -} +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/main/Utils.java b/src/de/deadlocker8/budgetmasterserver/main/Utils.java deleted file mode 100644 index 417375ffc23b3164fd3e54bbb54fc37c73534438..0000000000000000000000000000000000000000 --- a/src/de/deadlocker8/budgetmasterserver/main/Utils.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.deadlocker8.budgetmasterserver.main; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; - -import com.google.gson.Gson; - -public class Utils -{ - public static Settings loadSettings() throws IOException - { - String settingsJSON; - Settings settings; - - Gson gson = new Gson(); - settingsJSON = new String(Files.readAllBytes(Paths.get("settings.json"))); - settings = gson.fromJson(settingsJSON, Settings.class); - return settings; - } -} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/main/_de.properties b/src/de/deadlocker8/budgetmasterserver/main/_de.properties index b231f1d78f4e46819869a44ca52fbc7a2efe6b88..a3d8467008e914df260865c3283be6d831e09e01 100644 --- a/src/de/deadlocker8/budgetmasterserver/main/_de.properties +++ b/src/de/deadlocker8/budgetmasterserver/main/_de.properties @@ -1,5 +1,5 @@ app.name=BudgetMasterServer -version.code=2 -version.name=1.1.0 -version.date=12.04.17 +version.code=3 +version.name=1.2.0 +version.date=25.05.17 author=Robert Goldmann \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java b/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java index 760e6fb7424d0554da7e51d061f5606d4114d328..b2389dd932e79e399f09272e5140ae9d2b87e3e3 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java +++ b/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java @@ -17,14 +17,20 @@ import org.joda.time.DateTime; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; -import de.deadlocker8.budgetmasterserver.main.Settings; +import de.deadlocker8.budgetmaster.logic.Helpers; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.Settings; import de.deadlocker8.budgetmasterserver.server.category.CategoryAdd; import de.deadlocker8.budgetmasterserver.server.category.CategoryDelete; import de.deadlocker8.budgetmasterserver.server.category.CategoryGet; 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.database.DatabaseDelete; +import de.deadlocker8.budgetmasterserver.server.database.DatabaseExport; +import de.deadlocker8.budgetmasterserver.server.database.DatabaseImport; import de.deadlocker8.budgetmasterserver.server.payment.normal.PaymentAdd; import de.deadlocker8.budgetmasterserver.server.payment.normal.PaymentDelete; import de.deadlocker8.budgetmasterserver.server.payment.normal.PaymentGet; @@ -38,6 +44,7 @@ import de.deadlocker8.budgetmasterserver.server.updater.RepeatingPaymentUpdater; import logger.Logger; import spark.Spark; import spark.route.RouteOverview; +import tools.HashUtils; public class SparkServer { @@ -77,7 +84,7 @@ public class SparkServer String clientSecret = request.queryMap("secret").value(); - if(clientSecret == null || !clientSecret.equals(settings.getServerSecret())) + if(clientSecret == null || !clientSecret.equals(HashUtils.hash(settings.getServerSecret(), Helpers.SALT))) { halt(401, "Unauthorized"); } @@ -109,7 +116,16 @@ public class SparkServer get("/categorybudget", new CategoryBudgetGet(handler, gson)); // Rest - get("/rest", new RestGet(handler, gson)); + get("/rest", new RestGet(handler, gson)); + + //charts + get("/charts/categoryInOutSum", new CategoryInOutSumForMonth(handler, gson)); + get("/charts/monthInOutSum", new MonthInOutSum(handler, gson)); + + // Database + get("/database", new DatabaseExport(settings, gson)); + post("/database", new DatabaseImport(handler, gson)); + delete("/database", new DatabaseDelete(handler, settings)); after((request, response) -> { new RepeatingPaymentUpdater(handler).updateRepeatingPayments(DateTime.now()); diff --git a/src/de/deadlocker8/budgetmasterserver/server/category/CategoryAdd.java b/src/de/deadlocker8/budgetmasterserver/server/category/CategoryAdd.java index c20739e619a6090aacd41e94d79ef9178600cbca..71e5dcac445853179abdf3aa35ed2cff6c1ae875 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/category/CategoryAdd.java +++ b/src/de/deadlocker8/budgetmasterserver/server/category/CategoryAdd.java @@ -2,7 +2,7 @@ package de.deadlocker8.budgetmasterserver.server.category; import static spark.Spark.halt; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import javafx.scene.paint.Color; import spark.Request; import spark.Response; diff --git a/src/de/deadlocker8/budgetmasterserver/server/category/CategoryDelete.java b/src/de/deadlocker8/budgetmasterserver/server/category/CategoryDelete.java index 3a0088e2a9205c1d5b786d0b6e9782b3a3d1edca..5e4ad5a6958cf02678651d6e06a0e25e08311980 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/category/CategoryDelete.java +++ b/src/de/deadlocker8/budgetmasterserver/server/category/CategoryDelete.java @@ -2,7 +2,7 @@ package de.deadlocker8.budgetmasterserver.server.category; import static spark.Spark.halt; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import spark.Request; import spark.Response; import spark.Route; diff --git a/src/de/deadlocker8/budgetmasterserver/server/category/CategoryGet.java b/src/de/deadlocker8/budgetmasterserver/server/category/CategoryGet.java index 55953ad73dd19db8f21cc266fc44908d3b4d6041..bf4eb9c41614aff11d4b2173893822656b86a3e3 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/category/CategoryGet.java +++ b/src/de/deadlocker8/budgetmasterserver/server/category/CategoryGet.java @@ -5,7 +5,7 @@ import static spark.Spark.halt; import com.google.gson.Gson; import de.deadlocker8.budgetmaster.logic.Category; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import spark.Request; import spark.Response; import spark.Route; diff --git a/src/de/deadlocker8/budgetmasterserver/server/category/CategoryGetAll.java b/src/de/deadlocker8/budgetmasterserver/server/category/CategoryGetAll.java index 75a921947320ca5ff99c070aeabaf8441a89cd08..8bb67cab65a1a3fb7346ef9cc3e9139ef099f18e 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/category/CategoryGetAll.java +++ b/src/de/deadlocker8/budgetmasterserver/server/category/CategoryGetAll.java @@ -7,7 +7,7 @@ import java.util.ArrayList; import com.google.gson.Gson; import de.deadlocker8.budgetmaster.logic.Category; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import spark.Request; import spark.Response; import spark.Route; diff --git a/src/de/deadlocker8/budgetmasterserver/server/category/CategoryUpdate.java b/src/de/deadlocker8/budgetmasterserver/server/category/CategoryUpdate.java index 315199436e410d4d7c8bfd114fa030ae6d033e33..a8f4d65dddc92caae6953173d6b0c6a367799a0f 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/category/CategoryUpdate.java +++ b/src/de/deadlocker8/budgetmasterserver/server/category/CategoryUpdate.java @@ -2,7 +2,7 @@ package de.deadlocker8.budgetmasterserver.server.category; import static spark.Spark.halt; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import javafx.scene.paint.Color; import spark.Request; import spark.Response; diff --git a/src/de/deadlocker8/budgetmasterserver/server/categorybudget/CategoryBudgetGet.java b/src/de/deadlocker8/budgetmasterserver/server/categorybudget/CategoryBudgetGet.java index 0b0859c69771c6405bf01921b09e09928f6ea535..26504559b0cd744843a9c32b574bb039cdfe1c9d 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/categorybudget/CategoryBudgetGet.java +++ b/src/de/deadlocker8/budgetmasterserver/server/categorybudget/CategoryBudgetGet.java @@ -12,7 +12,7 @@ import com.google.gson.Gson; import de.deadlocker8.budgetmaster.logic.Category; import de.deadlocker8.budgetmaster.logic.CategoryBudget; import de.deadlocker8.budgetmaster.logic.Payment; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import spark.Request; import spark.Response; import spark.Route; @@ -54,14 +54,7 @@ public class CategoryBudgetGet implements Route ArrayList<Payment> payments = new ArrayList<>(); payments.addAll(handler.getPayments(year, month)); payments.addAll(handler.getRepeatingPayments(year, month)); - Collections.sort(payments, new Comparator<Payment>() { - @Override - public int compare(Payment payment1, Payment payment2) - { - return payment2.getDate().compareTo(payment1.getDate()); - } - }); - + ArrayList<CategoryBudget> budgets = new ArrayList<>(); for(Category currentCategory : handler.getCategories()) diff --git a/src/de/deadlocker8/budgetmasterserver/server/charts/CategoryInOutSumForMonth.java b/src/de/deadlocker8/budgetmasterserver/server/charts/CategoryInOutSumForMonth.java new file mode 100644 index 0000000000000000000000000000000000000000..067567b4a9658742dc33d19a70c0840a5d0b9d58 --- /dev/null +++ b/src/de/deadlocker8/budgetmasterserver/server/charts/CategoryInOutSumForMonth.java @@ -0,0 +1,74 @@ +package de.deadlocker8.budgetmasterserver.server.charts; + +import static spark.Spark.halt; + +import java.util.ArrayList; + +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.logic.DatabaseHandler; +import spark.Request; +import spark.Response; +import spark.Route; + +public class CategoryInOutSumForMonth implements Route +{ + private DatabaseHandler handler; + private Gson gson; + + public CategoryInOutSumForMonth(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 + { + ArrayList<Payment> payments = new ArrayList<>(); + payments.addAll(handler.getPaymentsBetween(req.queryMap("startDate").value(), req.queryMap("endDate").value())); + payments.addAll(handler.getRepeatingPaymentsBetween(req.queryMap("startDate").value(), req.queryMap("endDate").value())); + + ArrayList<CategoryInOutSum> inOutSums = new ArrayList<>(); + + for(Category currentCategory : handler.getCategories()) + { + inOutSums.add(new CategoryInOutSum(currentCategory.getID(), currentCategory.getName(), currentCategory.getColor(), 0, 0)); + CategoryInOutSum currentInOutSum = inOutSums.get(inOutSums.size() - 1); + for(Payment currentPayment : payments) + { + if(currentCategory.getID() == currentPayment.getCategoryID()) + { + int amount = currentPayment.getAmount(); + if(amount > 0) + { + currentInOutSum.setBudgetIN(currentInOutSum.getBudgetIN() + amount); + } + else + { + currentInOutSum.setBudgetOUT(currentInOutSum.getBudgetOUT() + amount); + } + } + } + } + + return gson.toJson(inOutSums); + } + catch(IllegalStateException ex) + { + halt(500, "Internal Server Error"); + } + + return null; + } +} \ No newline at end of file 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..452e9acdffb3c820c9dff64d7600796659edb1ce --- /dev/null +++ b/src/de/deadlocker8/budgetmasterserver/server/charts/MonthInOutSum.java @@ -0,0 +1,88 @@ +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.Category; +import de.deadlocker8.budgetmaster.logic.CategoryInOutSum; +import de.deadlocker8.budgetmaster.logic.Payment; +import de.deadlocker8.budgetmasterserver.logic.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())); + + 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(), sums)); + + startDate = startDate.plusMonths(1); + } + + return gson.toJson(monthInOutSums); + } + catch(IllegalStateException ex) + { + halt(500, "Internal Server Error"); + } + + return null; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/server/database/DatabaseDelete.java b/src/de/deadlocker8/budgetmasterserver/server/database/DatabaseDelete.java new file mode 100644 index 0000000000000000000000000000000000000000..373a788ade424ea1cbb62c35632a58a82efbcc6a --- /dev/null +++ b/src/de/deadlocker8/budgetmasterserver/server/database/DatabaseDelete.java @@ -0,0 +1,39 @@ +package de.deadlocker8.budgetmasterserver.server.database; + +import static spark.Spark.halt; + +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.Settings; +import spark.Request; +import spark.Response; +import spark.Route; + +public class DatabaseDelete implements Route +{ + private DatabaseHandler handler; + private Settings settings; + + public DatabaseDelete(DatabaseHandler handler, Settings settings) + { + this.handler = handler; + this.settings = settings; + } + + @Override + public Object handle(Request req, Response res) throws Exception + { + try + { + handler.deleteDatabase(); + handler = new DatabaseHandler(settings); + + return ""; + } + catch(IllegalStateException ex) + { + halt(500, "Internal Server Error"); + } + + return ""; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/server/database/DatabaseExport.java b/src/de/deadlocker8/budgetmasterserver/server/database/DatabaseExport.java new file mode 100644 index 0000000000000000000000000000000000000000..5205de6f69fdc68ff3988e23ab85e2502fe18422 --- /dev/null +++ b/src/de/deadlocker8/budgetmasterserver/server/database/DatabaseExport.java @@ -0,0 +1,41 @@ +package de.deadlocker8.budgetmasterserver.server.database; + +import static spark.Spark.halt; + +import com.google.gson.Gson; + +import de.deadlocker8.budgetmasterserver.logic.DatabaseExporter; +import de.deadlocker8.budgetmasterserver.logic.Settings; +import logger.Logger; +import spark.Request; +import spark.Response; +import spark.Route; + +public class DatabaseExport implements Route +{ + private Settings settings; + private Gson gson; + + public DatabaseExport(Settings settings, Gson gson) + { + this.settings = settings; + this.gson = gson; + } + + @Override + public Object handle(Request req, Response res) throws Exception + { + try + { + DatabaseExporter exporter = new DatabaseExporter(settings); + return gson.toJson(exporter.exportDatabase()); + } + catch(Exception e) + { + Logger.error(e); + halt(500, "Internal Server Error"); + } + + return ""; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/server/database/DatabaseImport.java b/src/de/deadlocker8/budgetmasterserver/server/database/DatabaseImport.java new file mode 100644 index 0000000000000000000000000000000000000000..082e938ae41446f8814edddfb8c825f55f0af685 --- /dev/null +++ b/src/de/deadlocker8/budgetmasterserver/server/database/DatabaseImport.java @@ -0,0 +1,47 @@ +package de.deadlocker8.budgetmasterserver.server.database; + +import static spark.Spark.halt; + +import com.google.gson.Gson; + +import de.deadlocker8.budgetmasterserver.logic.Database; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseImporter; +import logger.Logger; +import spark.Request; +import spark.Response; +import spark.Route; + +public class DatabaseImport implements Route +{ + private DatabaseHandler handler; + private Gson gson; + + public DatabaseImport(DatabaseHandler handler, Gson gson) + { + this.handler = handler; + this.gson = gson; + } + + @Override + public Object handle(Request req, Response res) throws Exception + { + String databaseJSON = req.body(); + + try + { + Database database = gson.fromJson(databaseJSON, Database.class); + + DatabaseImporter importer = new DatabaseImporter(handler); + importer.importDatabase(database); + return ""; + } + catch(Exception e) + { + Logger.error(e); + halt(500, "Internal Server Error"); + } + + return ""; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentAdd.java b/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentAdd.java index 7a6bd99a68ba75c9e2a2f1f9d63ee63724359c7d..9d016915dc8eeb91a1575b480fc3549a7bd5b66b 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentAdd.java +++ b/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentAdd.java @@ -2,7 +2,7 @@ package de.deadlocker8.budgetmasterserver.server.payment.normal; import static spark.Spark.halt; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import spark.Request; import spark.Response; import spark.Route; diff --git a/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentDelete.java b/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentDelete.java index 0933a3ba5a32f45ea6f1edc0cbaf577efe2f9083..f5ff9285872dff0339bd02a6c15c787d0cb8f4d6 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentDelete.java +++ b/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentDelete.java @@ -2,7 +2,7 @@ package de.deadlocker8.budgetmasterserver.server.payment.normal; import static spark.Spark.halt; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import spark.Request; import spark.Response; import spark.Route; diff --git a/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentGet.java b/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentGet.java index 967cbae9fd99ba3e79197632bbb03a1fb4fe052d..b13dba89e08a7832ea8c0b152b2a5042899ea260 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentGet.java +++ b/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentGet.java @@ -9,7 +9,7 @@ import org.joda.time.DateTime; import com.google.gson.Gson; import de.deadlocker8.budgetmaster.logic.NormalPayment; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import de.deadlocker8.budgetmasterserver.server.updater.RepeatingPaymentUpdater; import spark.Request; import spark.Response; diff --git a/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentUpdate.java b/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentUpdate.java index 6eb56285f87f075493cb671996ad1b5f9a54de4c..3c58aa12cfe1c8a16a41b0b32ce2ccbe8599c5fa 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentUpdate.java +++ b/src/de/deadlocker8/budgetmasterserver/server/payment/normal/PaymentUpdate.java @@ -2,7 +2,7 @@ package de.deadlocker8.budgetmasterserver.server.payment.normal; import static spark.Spark.halt; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import spark.Request; import spark.Response; import spark.Route; diff --git a/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentAdd.java b/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentAdd.java index e55598dcfccac3b03bde656273124e49fca3fbf1..4d622ffb1179f671b11cc1534602838dcc77f029 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentAdd.java +++ b/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentAdd.java @@ -2,7 +2,7 @@ package de.deadlocker8.budgetmasterserver.server.payment.repeating; import static spark.Spark.halt; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import spark.Request; import spark.Response; import spark.Route; diff --git a/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentDelete.java b/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentDelete.java index b2b480c5d81f263a27b4340cf4f1616449d72df0..a88de42bffad9293f85214a4c0975ecda7fbb208 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentDelete.java +++ b/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentDelete.java @@ -2,7 +2,7 @@ package de.deadlocker8.budgetmasterserver.server.payment.repeating; import static spark.Spark.halt; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import spark.Request; import spark.Response; import spark.Route; diff --git a/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentGet.java b/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentGet.java index fc6e13d84f23a612fbf0cc3c05fd9d66add7c6b4..0a8fd5af5c87a92b58205a771eeebb2e6d13ca07 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentGet.java +++ b/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentGet.java @@ -5,7 +5,7 @@ import static spark.Spark.halt; import com.google.gson.Gson; import de.deadlocker8.budgetmaster.logic.RepeatingPayment; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import spark.Request; import spark.Response; import spark.Route; diff --git a/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentGetAll.java b/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentGetAll.java index 3c053d1142b95a5e2dadadafaafac1f817100e2c..573a3c0114fc94c3a366d5d1ea3a0f4435e8fbc9 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentGetAll.java +++ b/src/de/deadlocker8/budgetmasterserver/server/payment/repeating/RepeatingPaymentGetAll.java @@ -9,7 +9,7 @@ import org.joda.time.DateTime; import com.google.gson.Gson; import de.deadlocker8.budgetmaster.logic.RepeatingPaymentEntry; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import de.deadlocker8.budgetmasterserver.server.updater.RepeatingPaymentUpdater; import spark.Request; import spark.Response; diff --git a/src/de/deadlocker8/budgetmasterserver/server/rest/RestGet.java b/src/de/deadlocker8/budgetmasterserver/server/rest/RestGet.java index 0962749b594c2111f93c633685446494585704b7..2dd749311af34963b22d1d4a4149028a9ea40994 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/rest/RestGet.java +++ b/src/de/deadlocker8/budgetmasterserver/server/rest/RestGet.java @@ -4,7 +4,7 @@ import static spark.Spark.halt; import com.google.gson.Gson; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import spark.Request; import spark.Response; import spark.Route; diff --git a/src/de/deadlocker8/budgetmasterserver/server/updater/RepeatingPaymentUpdater.java b/src/de/deadlocker8/budgetmasterserver/server/updater/RepeatingPaymentUpdater.java index 0b7246a911ffcb06200b782759ce1c01eb3482d9..65299438637232fdaf169cf2d3fe9679016fa564 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/updater/RepeatingPaymentUpdater.java +++ b/src/de/deadlocker8/budgetmasterserver/server/updater/RepeatingPaymentUpdater.java @@ -8,7 +8,7 @@ import org.joda.time.Months; import de.deadlocker8.budgetmaster.logic.LatestRepeatingPayment; import de.deadlocker8.budgetmaster.logic.RepeatingPayment; -import de.deadlocker8.budgetmasterserver.main.DatabaseHandler; +import de.deadlocker8.budgetmasterserver.logic.DatabaseHandler; import logger.Logger; public class RepeatingPaymentUpdater