Skip to content
Snippets Groups Projects
Commit 22a4e768 authored by Robert Goldmann's avatar Robert Goldmann
Browse files

Fixed #662 - new chart: Income/Expenditures per year per categories

parent baddf238
No related branches found
No related tags found
No related merge requests found
Pipeline #5898 failed
...@@ -50,6 +50,10 @@ public class DefaultCharts ...@@ -50,6 +50,10 @@ public class DefaultCharts
getChartFromFile("charts/IncomesAndExpendituresPerYearBar.js"), getChartFromFile("charts/IncomesAndExpendituresPerYearBar.js"),
ChartType.DEFAULT, 6, ChartDisplayType.BAR, ChartGroupType.YEAR, "incomesAndExpendituresPerYearBar.png"); ChartType.DEFAULT, 6, ChartDisplayType.BAR, ChartGroupType.YEAR, "incomesAndExpendituresPerYearBar.png");
private static final Chart CHART_INCOMES_AND_EXPENDITURES_PER_YEAR_BY_CATEGORIES = new Chart("charts.default.incomesAndExpendituresPerYearByCategories",
getChartFromFile("charts/IncomesAndExpendituresPerYearByCategories.js"),
ChartType.DEFAULT, 2, ChartDisplayType.BAR, ChartGroupType.YEAR, "incomesAndExpendituresPerYearByCategories.png");
private DefaultCharts() private DefaultCharts()
{ {
} }
...@@ -65,6 +69,7 @@ public class DefaultCharts ...@@ -65,6 +69,7 @@ public class DefaultCharts
charts.add(CHART_INCOMES_AND_EXPENDITURES_PER_MONTH_BY_CATEGORIES); charts.add(CHART_INCOMES_AND_EXPENDITURES_PER_MONTH_BY_CATEGORIES);
charts.add(CHART_REST_PER_MONTH); charts.add(CHART_REST_PER_MONTH);
charts.add(CHART_INCOMES_AND_EXPENDITURES_PER_YEAR_BAR); charts.add(CHART_INCOMES_AND_EXPENDITURES_PER_YEAR_BAR);
charts.add(CHART_INCOMES_AND_EXPENDITURES_PER_YEAR_BY_CATEGORIES);
return charts; return charts;
} }
...@@ -83,7 +88,7 @@ public class DefaultCharts ...@@ -83,7 +88,7 @@ public class DefaultCharts
} }
catch(IOException e) catch(IOException e)
{ {
e.printStackTrace(); LOGGER.error("Error getting chart from file", e);
} }
return ""; return "";
......
/* 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.
transactionData = transactionData.reverse();
moment.locale('de');
const NAME = 0;
const COLOR = 1;
const INCOME = 2;
const EXPENDITURE = 3;
var categoryNames = [];
var categoryColors = [];
for(var i = 0; i < transactionData.length; i++)
{
var currentTransaction = transactionData[i];
if(!categoryNames.includes(currentTransaction.category.name))
{
categoryNames.push(currentTransaction.category.name);
categoryColors.push(currentTransaction.category.color);
}
}
var dates = [];
var values = [];
for(var i = 0; i < transactionData.length; i++)
{
var transaction = transactionData[i];
var date = moment(transaction.date).startOf('year').format('YYYY');
if(!dates.includes(date))
{
dates.push(date);
values.push([
categoryNames, // NAME
categoryColors, // COLOR
new Array(categoryNames.length).fill(0), // INCOME
new Array(categoryNames.length).fill(0) // EXPENDITURE
]);
}
// determine index of category name in list
var lastIndex = values.length - 1;
var categoryName = transaction.category.name;
// create new category if not already in dict
if(!values[lastIndex][NAME].includes(categoryName))
{
values[lastIndex][NAME].push(categoryName);
values[lastIndex][COLOR].push(transaction.category.color);
values[lastIndex][INCOME].push(0);
values[lastIndex][EXPENDITURE].push(0);
}
// determine index of category in current last values
var index = values[lastIndex][NAME].indexOf(categoryName);
// add to income or expenditure sum
var amount = transaction.amount;
if(amount > 0)
{
values[lastIndex][INCOME][index] = values[lastIndex][INCOME][index] + amount;
}
else
{
values[lastIndex][EXPENDITURE][index] = values[lastIndex][EXPENDITURE][index] + Math.abs(amount);
}
}
var totalIncomeSums = [];
var totalExpenditureSums = [];
// calculate total sums for all months
for(var i = 0; i < dates.length; i++)
{
var totalIncomes = 0;
var totalExpenditures = 0;
values[i][INCOME].forEach(function(value)
{
totalIncomes += value;
});
values[i][EXPENDITURE].forEach(function(value)
{
totalExpenditures += value;
});
totalIncomeSums.push(totalIncomes);
totalExpenditureSums.push(totalExpenditures);
}
// Prepare your chart settings here (mandatory)
var plotlyData = [];
var plotlyLayout = {
title: {
text: localizedTitle
},
barmode: "stack",
hovermode: 'closest', // show hover popup only for hovered item
yaxis: {
rangemode: 'tozero',
tickformat: '.0f',
ticksuffix: localizedCurrency,
showline: true
}
};
// create one stacked bar for incomes and one for expenditures for every month and group them by month
for(var i = 0; i < dates.length; i++)
{
for(var j = 0; j < values[i][NAME].length; j++)
{
var currentValues = values[i];
var currentName = currentValues[NAME][j];
var currentIncomeValue = currentValues[INCOME][j];
var percentageIncome = (100 / totalIncomeSums[i]) * currentIncomeValue;
var textIncome = prepareHoverText(currentName, percentageIncome, currentIncomeValue);
var currentExpenditureValue = currentValues[EXPENDITURE][j];
var percentageExpenditure = (100 / totalExpenditureSums[i]) * currentExpenditureValue;
var textExpenditure = prepareHoverText(currentName, percentageExpenditure, currentExpenditureValue);
// add border if category color is white
var borderWidth = 0;
if(currentValues[COLOR][j] === '#FFFFFF')
{
borderWidth = 1;
}
plotlyData.push({
x: [localizedData['label2'], localizedData['label1']],
y: [currentIncomeValue / 100.0, currentExpenditureValue / 100.0],
type: 'bar',
hoverinfo: 'text',
hovertext: [textIncome, textExpenditure],
name: currentName,
xaxis: 'x' + (i + 1), // for grouping incomes and expenditure bar by month
barmode: 'stack',
showlegend: i === 0,
legendgroup: currentName,
marker: {
color: currentValues[COLOR][j], // use the category's color
line: {
color: '#212121',
width: borderWidth
}
}
});
}
// axis number inside layout uses a different counting in comparison to xaxis definition in plotlyDate
var axisNumber = i + 1;
if(i === 0)
{
axisNumber = '';
}
// calculate subplot start and end position (relative between 0 and 1)
var width = 1 / dates.length;
var start = i * width;
var end = (i + 1) * width;
plotlyLayout['xaxis' + axisNumber] = {
domain: [start, end],
anchor: 'x' + axisNumber,
title: dates[i],
}
}
// 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("containerID", plotlyData, plotlyLayout, plotlyConfig);
function prepareHoverText(categoryName, percentage, value)
{
value = value / 100;
return categoryName + ' ' + percentage.toFixed(1) + '% (' + value.toFixed(1) + ' ' + localizedCurrency + ')';
}
\ No newline at end of file
...@@ -533,6 +533,8 @@ charts.default.restPerMonth=Rest ...@@ -533,6 +533,8 @@ charts.default.restPerMonth=Rest
charts.default.restPerMonth.localization='{"label1": "Rest in "'} charts.default.restPerMonth.localization='{"label1": "Rest in "'}
charts.default.incomesAndExpendituresPerYearBar=Eingaben/Ausgaben charts.default.incomesAndExpendituresPerYearBar=Eingaben/Ausgaben
charts.default.incomesAndExpendituresPerYearBar.localization='{"axisY": "Summe in ", "traceName1": "Einnahmen", "traceName2": "Ausgaben"'} charts.default.incomesAndExpendituresPerYearBar.localization='{"axisY": "Summe in ", "traceName1": "Einnahmen", "traceName2": "Ausgaben"'}
charts.default.incomesAndExpendituresPerYearByCategories=Eingaben/Ausgaben nach Kategorien
charts.default.incomesAndExpendituresPerYearByCategories.localization='{"label1": "Ausgaben", "label2": "Einnahmen"'}
chart.new.label.name=Name chart.new.label.name=Name
chart.new.label.script=Script chart.new.label.script=Script
......
...@@ -533,6 +533,8 @@ charts.default.restPerMonth=Rest ...@@ -533,6 +533,8 @@ charts.default.restPerMonth=Rest
charts.default.restPerMonth.localization='{"label1": "Rest in "'} charts.default.restPerMonth.localization='{"label1": "Rest in "'}
charts.default.incomesAndExpendituresPerYearBar=Incomes/Expenditures charts.default.incomesAndExpendituresPerYearBar=Incomes/Expenditures
charts.default.incomesAndExpendituresPerYearBar.localization='{"axisY": "Sum in ", "traceName1": "Incomes", "traceName2": "Expenditures"'} charts.default.incomesAndExpendituresPerYearBar.localization='{"axisY": "Sum in ", "traceName1": "Incomes", "traceName2": "Expenditures"'}
charts.default.incomesAndExpendituresPerYearByCategories=Incomes/Expenditures by categories
charts.default.incomesAndExpendituresPerYearByCategories.localization='{"label1": "Expenditures", "label2": "Incomes"'}
chart.new.label.name=Name chart.new.label.name=Name
chart.new.label.script=Script chart.new.label.script=Script
......
src/main/resources/static/images/charts/incomesAndExpendituresPerYearByCategories.png

30.1 KiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment