diff --git a/accountant/frontend/static/js/app.js b/accountant/frontend/static/js/app.js
index c409d07..6bba7e7 100644
--- a/accountant/frontend/static/js/app.js
+++ b/accountant/frontend/static/js/app.js
@@ -15,7 +15,8 @@
along with Accountant. If not, see .
*/
var accountantApp = angular.module("accountantApp", [
- "mgcrea.ngStrap"
+ "mgcrea.ngStrap",
+ "highcharts-ng"
])
.config(function($interpolateProvider, $httpProvider) {
diff --git a/accountant/frontend/static/js/entries.js b/accountant/frontend/static/js/entries.js
index de13ed9..9694b41 100644
--- a/accountant/frontend/static/js/entries.js
+++ b/accountant/frontend/static/js/entries.js
@@ -28,105 +28,151 @@ accountantApp.controller(
// Placeholder for saved value to cancel entry edition
$scope.savedItem = null;
- $scope.entriesLoaded = function(entries) {
- var entriesReversed = entries.slice().reverse();
-
- var categories = [];
-
- var chartValues = [];
-
- var pieChartValuesTmp = {};
- var pieChartValues = [];
-
- angular.forEach(entriesReversed, function(entry) {
- var category = entry.category;
- var value = entry.value ? Number(entry.value) : null;
- var sold = entry.sold ? Number(entry.sold) : null;
- var operation_date = entry.operation_date;
-
- if(operation_date && sold) {
- chartValues.push([entry.operation_date, sold]);
- }
-
- if(category && category != '') {
- if(categories.indexOf(category) == -1) {
- categories.push(category);
- }
-
- if(value && value < 0.0) {
- var oldValue = 0.0;
-
- if(pieChartValuesTmp[category]) {
- oldValue = pieChartValuesTmp[category];
+ // Configure pie chart for categories.
+ $scope.categoriesChartConfig = {
+ options: {
+ chart: {
+ type: 'pie',
+ animation: {
+ duration: 500
+ }
+ },
+ plotOptions: {
+ pie: {
+ startAngle: -90
+ }
+ },
+ tooltip: {
+ valueDecimals: 2,
+ valueSuffix: '€'
}
+ },
+ yAxis: {
+ title: {
+ text: "Categories"
+ }
+ },
+ title: {
+ text: "Répartition dépenses/recettes"
+ },
+ series: [{
+ name: "Value",
+ data: [],
+ innerSize: '33%',
+ size: '60%',
+ dataLabels: {
+ formatter: function() {
+ return this.point.name;
+ },
+ distance: -40
+ }
+ }, {
+ name: "Value",
+ data: [],
+ innerSize: '66%',
+ size: '60%',
+ dataLabels: {
+ formatter: function() {
+ return this.point.name !== null && this.y > 100 ? this.point.name : null;
+ },
+ }
+ }]
+ };
- pieChartValuesTmp[category] = oldValue - value;
- }
- }
- });
-
- $scope.chartValues = chartValues;
+ // Load categories, mainly to populate the pie chart.
+ $scope.loadCategories = function() {
+ $http.get("/api/categories", {
+ params: {
+ account: $scope.account.id,
+ begin: $scope.begin.format('YYYY-MM-DD'),
+ end: $scope.end.format('YYYY-MM-DD')
+ }
+ }).success(function(data) {
+ var expenses = [], revenues = [];
+ var colors = Highcharts.getOptions().colors;
- // Second pass: transform to an array readable by jqplot.
- var pieChartValues = [];
- angular.forEach(pieChartValuesTmp, function(value, key) {
- pieChartValues.push([key, value]);
- });
+ var config = $scope.categoriesChartConfig;
- $scope.pieChartValues = pieChartValues;
+ angular.forEach(angular.fromJson(data), function(category) {
+ brightness = 0.2;
- //$scope.categories.length = 0;
- //$scope.categories.concat(categories);
- $scope.categories = categories;
+ expenses.push({
+ name: category.category,
+ y: -category.expenses,
+ color: Highcharts.Color(config.series[0].data[1].color).brighten(brightness).get()
+ });
- nv.addGraph($scope.drawChart);
- nv.addGraph($scope.drawPieChart);
- //$scope.drawPieChart(pieChartValues, "#expense-categories-chart-placeholder");
- //$scope.drawPieChart();
+ revenues.push({
+ name: category.category,
+ y: category.revenues,
+ color: Highcharts.Color(config.series[0].data[0].color).brighten(brightness).get()
+ });
+ });
+
+ // Note: expenses and revenues must be in the same order than in series[0].
+ config.series[1].data = revenues.concat(expenses);
+ });
};
$scope.getAccountStatus = function(account, month) {
- if(account != null && month != null) {
- // Note: Month is 0 indexed.
- var begin = moment({year: month.year, month: month.month - 1, day: 1});
- var end = begin.clone().endOf('month');
+ // Note: Month is 0 indexed.
+ $scope.begin = moment({year: month.year, month: month.month - 1, day: 1});
+ $scope.end = $scope.begin.clone().endOf('month');
- $http.get("/api/accounts/" + account.id,
- {params: {
- begin: begin.format('YYYY-MM-DD'),
- end: end.format('YYYY-MM-DD')
- }}).success($scope.getAccountStatus_success);
- }
- };
+ $http.get("/api/accounts/" + account.id, {
+ params: {
+ begin: $scope.begin.format('YYYY-MM-DD'),
+ end: $scope.end.format('YYYY-MM-DD')
+ }
+ }).success(function(status) {
+ var colors = Highcharts.getOptions().colors;
- $scope.getAccountStatus_success = function(status) {
- $scope.accountStatus = status;
+ var config = $scope.categoriesChartConfig;
+
+ // Update pie chart subtitle with Balance.
+ config.subtitle = {
+ text: "Balance: " + status.balance
+ };
+
+ config.series[0].data = [{
+ name: "Revenues",
+ y: status.revenues,
+ color: colors[2]
+ }, {
+ name: "Expenses",
+ y: -status.expenses,
+ color: colors[3],
+ }];
+
+ $scope.loadCategories();
+ });
};
// Function to load entries from server for a specific account and month.
$scope.loadEntries = function(account, month) {
- if(account) {
- $scope.account = account;
- }
+ if(account) {
+ $scope.account = account;
+ }
- // Clean up selected entry.
- $scope.selectedItem = null;
- $scope.savedItem = null;
+ // Clean up selected entry.
+ $scope.selectedItem = null;
+ $scope.savedItem = null;
- if(account && month) {
- // Note: Month is 0 indexed.
- var begin = moment({year: month.year, month: month.month - 1, day: 1});
- var end = begin.clone().endOf('month');
+ if(account && month) {
+ // Note: Month is 0 indexed.
+ var begin = moment({year: month.year, month: month.month - 1, day: 1});
+ var end = begin.clone().endOf('month');
- $http.get("/api/entries",
- {params: {
- account: account.id,
- begin: begin.format('YYYY-MM-DD'),
- end: end.format('YYYY-MM-DD')
- }}).success($scope.loadEntries_success);
- } else {
- $scope.loadEntries_success(null);
- }
+ $http.get("/api/entries", {
+ params: {
+ account: account.id,
+ begin: begin.format('YYYY-MM-DD'),
+ end: end.format('YYYY-MM-DD')
+ }
+ }).success($scope.loadEntries_success);
+ } else {
+ $scope.loadEntries_success(null);
+ }
};
// Load entries success callback
@@ -362,97 +408,6 @@ accountantApp.controller(
}
};
- // Function to draw the sold evolution chart.
- $scope.drawChart = function() {
- // Clear previous chart
- var entries = $scope.chartValues;
- console.debug("drawChart", entries);
-
- var width = 700;
- var height = 300;
-
- //if(entries && entries.length > 1) {
- // Prepare for today vertical line.
- var today = new Date();
- today.setHours(0);
- today.setMinutes(0);
-
- // FIXME Alexis Lahouze 2015-06-12 Date format.
-
- // Find first and last days to set limits of the x axis.
- var firstDate = moment(entries[0][0]).subtract(1, 'day').format();
- var lastDate = moment(entries[entries.length - 1][0]).add(1, 'day').format();
-
- var chart = nv.models.lineChart().options({
- x: function(d) { return new Date(d[0]); },
- y: function(d) { return new Number(d[1]); },
- transitionDuration: 250,
- showXAxis: true,
- showYAxis: true,
- width: width,
- height: height,
- });
-
- chart.lines.interpolate("monotone");
-
- chart.xAxis
- .axisLabel("Date")
- .tickFormat(function(d) {
- return d3.time.format("%x")(new Date(d));
- });
- //chart.xAxis.scale().range([firstDate, lastDate]);
-
- chart.yAxis
- .axisLabel("Solde")
- .tickFormat(d3.format('.02f'));
-
- // FIXME add vertical line for today
- graph = d3.select("#entries-chart-placeholder").datum([
- { color: "orange", key: "Zero", values:[
- [firstDate, "0"],
- [lastDate, "0"]
- ]},
- { color: "red", key: "Authorized overdraft", values : [
- [firstDate, new Number($scope.account.authorized_overdraft)],
- [lastDate, new Number($scope.account.authorized_overdraft)]
- ]},
- { color: "darkblue", key: "Sold evolution", values: entries},
- ])
- .transition().duration(1200)
- .attr("width", width)
- .attr("height", height)
- .call(chart);
-
- nv.utils.windowResize(chart.update);
-
- return chart;
- };
-
- // Function to draw the expense category pie chart.
- $scope.drawPieChart = function() {
- // FIXME retrieve width and height from DOM
- var width = 300;
- var height = 300;
-
- var chart = nv.models.pieChart()
- .x(function(d) { console.debug(d); return d[0]; })
- .y(function(d) { return d[1]; })
- .width(width)
- .height(height)
- .showLabels(true);
-
- d3.select("#expense-categories-chart-placeholder")
- .datum($scope.pieChartValues)
- .transition().duration(1200)
- .attr('width', width)
- .attr('height', height)
- .call(chart);
-
- nv.utils.windowResize(chart.update);
-
- return chart
- };
-
$rootScope.$on("monthsLoadedEvent", function(event, args){
$scope.loadEntries(args.account, args.month);
});
@@ -460,8 +415,4 @@ accountantApp.controller(
$rootScope.$on("monthsLoadedEvent", function(event, args){
$scope.getAccountStatus(args.account, args.month);
});
-
- $scope.$on("entriesLoadedEvent", function(event, args) {
- $scope.entriesLoaded(args.entries);
- });
});
diff --git a/accountant/frontend/templates/index.html b/accountant/frontend/templates/index.html
index 8c47f9a..19890b3 100644
--- a/accountant/frontend/templates/index.html
+++ b/accountant/frontend/templates/index.html
@@ -20,25 +20,10 @@
-
-
-
+
-
-
-
-
-
-
-
-
- Dépenses : | [[accountStatus.expenses]] |
- Recettes : | [[accountStatus.revenues]] |
- Balance : | [[accountStatus.balance]] |
-
-
-
+