diff --git a/src/main/java/de/deadlocker8/budgetmaster/charts/DefaultCharts.java b/src/main/java/de/deadlocker8/budgetmaster/charts/DefaultCharts.java index ea52659f80242eaae8564b39f53bee9a104f15c2..b21614347f8239ef404c9467283cbfae69787e7f 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/charts/DefaultCharts.java +++ b/src/main/java/de/deadlocker8/budgetmaster/charts/DefaultCharts.java @@ -31,7 +31,11 @@ public class DefaultCharts private static final Chart CHART_INCOMES_AND_EXPENDITURES_BY_CATEGORY_BAR = new Chart("charts.default.incomesAndExpendituresByCategoryBar", getChartFromFile("charts/IncomesAndExpendituresByCategoryBar.js"), - ChartType.DEFAULT, 23); + ChartType.DEFAULT, 1); + + private static final Chart CHART_INCOMES_AND_EXPENDITURES_BY_CATEGORY_PIE = new Chart("charts.default.incomesAndExpendituresByCategoryPie", + getChartFromFile("charts/IncomesAndExpendituresByCategoryPie.js"), + ChartType.DEFAULT, 2); private static final Chart CHART_INCOMES_AND_EXPENDITURES_PER_MONTH_BY_CATEGORIES = new Chart("charts.default.incomesAndExpendituresPerMonthByCategories", getChartFromFile("charts/IncomesAndExpendituresPerMonthByCategories.js"), @@ -45,6 +49,7 @@ public class DefaultCharts charts.add(CHART_INCOMES_AND_EXPENDITURES_PER_MONTH_BAR); charts.add(CHART_INCOMES_AND_EXPENDITURES_PER_MONTH_LINE); charts.add(CHART_INCOMES_AND_EXPENDITURES_BY_CATEGORY_BAR); + charts.add(CHART_INCOMES_AND_EXPENDITURES_BY_CATEGORY_PIE); charts.add(CHART_INCOMES_AND_EXPENDITURES_PER_MONTH_BY_CATEGORIES); charts.sort(Comparator.comparing(Chart::getName)); diff --git a/src/main/resources/charts/IncomesAndExpendituresByCategoryPie.js b/src/main/resources/charts/IncomesAndExpendituresByCategoryPie.js new file mode 100644 index 0000000000000000000000000000000000000000..ee9efd07b001f8c9639e286671c6d5b2b3b4d4b8 --- /dev/null +++ b/src/main/resources/charts/IncomesAndExpendituresByCategoryPie.js @@ -0,0 +1,206 @@ +/* This list will be dynamically filled with all the transactions between + * the start and and date you select on the "Show Chart" page + * and filtered according to your specified filter. + * An example entry for this list and tutorial about how to create custom charts ca be found in the BudgetMaster wiki: + * https://github.com/deadlocker8/BudgetMaster/wiki/How-to-create-custom-charts + */ +var transactionData = []; + +// Note: All variables starting with "localized" are only available inside default charts. + +// objects for easier handling of categories with corresponding sums +class CategorySum +{ + constructor(categoryName, color) + { + this.categoryName = categoryName; + this.color = color; + this.amount = 0; + } + + add(amount) + { + this.amount += amount; + } +} +CategorySum.prototype.toString = function(){return this.categoryName + " , " + this.amount;}; + +class CategorySumHandler +{ + constructor() + { + this.sums = []; + } + + getByCategoryName(categoryName) + { + return this.sums.find(obj => { + return obj.categoryName === categoryName + }); + } + + add(categorySum) + { + this.sums.push(categorySum); + } + + getTotalSum() + { + var total = 0; + this.sums.forEach(function(sum) + { + total += sum.amount; + }); + return total; + } + + static sortNumber(a, b) + { + return a.amount - b.amount; + } + + getSumsSorted() + { + var sumsSorted = this.sums.filter(function(item){ + return item.amount !== 0; + }); + + return sumsSorted.sort(CategorySumHandler.sortNumber); + } +} + +var incomeHandler = new CategorySumHandler(); +var expenditureHandler = new CategorySumHandler(); + +for(var i = 0; i < transactionData.length; i++) +{ + var transaction = transactionData[i]; + + var categoryName = transaction.category.name; + // create new category if not already in dict + if(incomeHandler.getByCategoryName(categoryName) === undefined) + { + incomeHandler.add(new CategorySum(categoryName, transaction.category.color)); + } + + if(expenditureHandler.getByCategoryName(categoryName) === undefined) + { + expenditureHandler.add(new CategorySum(categoryName, transaction.category.color)); + } + + var amount = transaction.amount; + if(amount > 0) + { + incomeHandler.getByCategoryName(categoryName).add(amount); + } + else + { + expenditureHandler.getByCategoryName(categoryName).add(Math.abs(amount)); + } +} + +// Prepare your chart settings here (mandatory) +var plotlyData = addSeries(incomeHandler, 0); +plotlyData = plotlyData.concat(addSeries(expenditureHandler, 1)); + +// Add your Plotly layout settings here (optional) +var plotlyLayout = { + title: { + text: localizedTitle + }, + hovermode: 'closest', // show hover popup only for hovered item + grid: {rows: 1, columns: 2}, + annotations: [ + { + font: { + size: 20 + }, + xref: "paper", + yref: "paper", + xanchor: "center", + yanchor: "top", + showarrow: false, + text: localizedData['label2'], + x: 0.25, + y: 0 + }, + { + font: { + size: 20 + }, + xref: "paper", + yref: "paper", + xanchor: "center", + yanchor: "top", + showarrow: false, + text: localizedData['label1'], + x: 0.75, + y: 0 + } + ], +}; + +// Add your Plotly configuration settings here (optional) +var plotlyConfig = { + showSendToCloud: false, + displaylogo: false, + showLink: false, + responsive: true, + displayModeBar: true, + toImageButtonOptions: { + format: 'png', + filename: 'BudgetMaster_chart_export', + height: 1080, + width: 1920, + } +}; + +// Don't touch this line +Plotly.newPlot('chart-canvas', plotlyData, plotlyLayout, plotlyConfig); + +function addSeries(sumHandler, column) +{ + var plotlyData = []; + + var total = sumHandler.getTotalSum(); + var sums = sumHandler.getSumsSorted(); + + var values = sums.map(item => item.amount); + var colors = sums.map(item => item.color); + var labels = sums.map(item => item.categoryName); + var hoverTexts = sums.map((item, index) => ` +${labels[index]}<br> +${(values[index]/100).toFixed(1) + ' ' + localizedCurrency}<br> +${(values[index] / total * 100).toFixed(1)}% +`); + + var borders = sums.map(item => { + if(item.color === '#FFFFFF') + { + return 1; + } + return 0; + }); + + plotlyData.push({ + values: values, + labels: labels, + type: 'pie', + text: hoverTexts, + hoverinfo: 'text', + textinfo: 'none', + marker: { + colors: colors, + line: { + color: '#212121', + width: borders + } + }, + domain: { + row: 0, + column: column, + }, + }); + + return plotlyData; +} \ No newline at end of file diff --git a/src/main/resources/languages/_de.properties b/src/main/resources/languages/_de.properties index a353e03dba250b4d6f23318542f56d06eaef2d4d..010f7b4ee5d4812c20e4406d463ecafdbb914985 100644 --- a/src/main/resources/languages/_de.properties +++ b/src/main/resources/languages/_de.properties @@ -335,8 +335,10 @@ charts.default.incomesAndExpendituresPerMonthBar=Eingaben/Ausgaben pro Monat (Ba charts.default.incomesAndExpendituresPerMonthBar.localization='{"axisY": "Summe in ", "traceName1": "Einnahmen", "traceName2": "Ausgaben"'} charts.default.incomesAndExpendituresPerMonthLine=Eingaben/Ausgaben pro Monat (Liniendiagramm) charts.default.incomesAndExpendituresPerMonthLine.localization='{"axisY": "Summe in ", "traceName1": "Einnahmen", "traceName2": "Ausgaben"'} -charts.default.incomesAndExpendituresByCategoryBar=Eingaben/Ausgaben nach Kategorien +charts.default.incomesAndExpendituresByCategoryBar=Eingaben/Ausgaben nach Kategorien (Balkendiagramm) charts.default.incomesAndExpendituresByCategoryBar.localization='{"label1": "Ausgaben", "label2": "Einnahmen"'} +charts.default.incomesAndExpendituresByCategoryPie=Eingaben/Ausgaben nach Kategorien (Tortendiagramm) +charts.default.incomesAndExpendituresByCategoryPie.localization='{"label1": "Ausgaben", "label2": "Einnahmen"'} charts.default.incomesAndExpendituresPerMonthByCategories=Eingaben/Ausgaben pro Monat (nach Kategorien) charts.default.incomesAndExpendituresPerMonthByCategories.localization='{"label1": "Ausgaben", "label2": "Einnahmen"'} charts.default.categoryBudget=Verbrauch nach Kategorien diff --git a/src/main/resources/languages/_en.properties b/src/main/resources/languages/_en.properties index 8e43cda688ab7e99c2c3008ae1433f54c7b05fdf..bf2200b789a686b8ccf7eb98eebdc268e79a42ff 100644 --- a/src/main/resources/languages/_en.properties +++ b/src/main/resources/languages/_en.properties @@ -335,8 +335,10 @@ charts.default.incomesAndExpendituresPerMonthBar=Incomes/Expenditures per month charts.default.incomesAndExpendituresPerMonthBar.localization='{"axisY": "Sum in ", "traceName1": "Incomes", "traceName2": "Expenditures"'} charts.default.incomesAndExpendituresPerMonthLine=Incomes/Expenditures per month (Line chart) charts.default.incomesAndExpendituresPerMonthLine.localization='{"axisY": "Sum in ", "traceName1": "Incomes", "traceName2": "Expenditures"'} -charts.default.incomesAndExpendituresByCategoryBar=Incomes/Expenditures by categories +charts.default.incomesAndExpendituresByCategoryBar=Incomes/Expenditures by categories (Bar chart) charts.default.incomesAndExpendituresByCategoryBar.localization='{"label1": "Expenditures", "label2": "Incomes"'} +charts.default.incomesAndExpendituresByCategoryPie=Incomes/Expenditures by categories (Pie chart) +charts.default.incomesAndExpendituresByCategoryPie.localization='{"label1": "Expenditures", "label2": "Incomes"'} charts.default.incomesAndExpendituresPerMonthByCategories=Incomes/Expenditures per month (By categories) charts.default.incomesAndExpendituresPerMonthByCategories.localization='{"label1": "Expenditures", "label2": "Incomes"'} charts.default.categoryBudget=Consumption by categories