Use highcharts instead of nvd3.
This commit is contained in:
parent
2131d1f39a
commit
fa981d4a32
@ -15,7 +15,8 @@
|
|||||||
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
var accountantApp = angular.module("accountantApp", [
|
var accountantApp = angular.module("accountantApp", [
|
||||||
"mgcrea.ngStrap"
|
"mgcrea.ngStrap",
|
||||||
|
"highcharts-ng"
|
||||||
])
|
])
|
||||||
|
|
||||||
.config(function($interpolateProvider, $httpProvider) {
|
.config(function($interpolateProvider, $httpProvider) {
|
||||||
|
@ -28,105 +28,151 @@ accountantApp.controller(
|
|||||||
// Placeholder for saved value to cancel entry edition
|
// Placeholder for saved value to cancel entry edition
|
||||||
$scope.savedItem = null;
|
$scope.savedItem = null;
|
||||||
|
|
||||||
$scope.entriesLoaded = function(entries) {
|
// Configure pie chart for categories.
|
||||||
var entriesReversed = entries.slice().reverse();
|
$scope.categoriesChartConfig = {
|
||||||
|
options: {
|
||||||
var categories = [];
|
chart: {
|
||||||
|
type: 'pie',
|
||||||
var chartValues = [];
|
animation: {
|
||||||
|
duration: 500
|
||||||
var pieChartValuesTmp = {};
|
}
|
||||||
var pieChartValues = [];
|
},
|
||||||
|
plotOptions: {
|
||||||
angular.forEach(entriesReversed, function(entry) {
|
pie: {
|
||||||
var category = entry.category;
|
startAngle: -90
|
||||||
var value = entry.value ? Number(entry.value) : null;
|
}
|
||||||
var sold = entry.sold ? Number(entry.sold) : null;
|
},
|
||||||
var operation_date = entry.operation_date;
|
tooltip: {
|
||||||
|
valueDecimals: 2,
|
||||||
if(operation_date && sold) {
|
valueSuffix: '€'
|
||||||
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];
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
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;
|
// 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;
|
||||||
|
|
||||||
$scope.chartValues = chartValues;
|
var config = $scope.categoriesChartConfig;
|
||||||
|
|
||||||
// Second pass: transform to an array readable by jqplot.
|
angular.forEach(angular.fromJson(data), function(category) {
|
||||||
var pieChartValues = [];
|
brightness = 0.2;
|
||||||
angular.forEach(pieChartValuesTmp, function(value, key) {
|
|
||||||
pieChartValues.push([key, value]);
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.pieChartValues = pieChartValues;
|
expenses.push({
|
||||||
|
name: category.category,
|
||||||
|
y: -category.expenses,
|
||||||
|
color: Highcharts.Color(config.series[0].data[1].color).brighten(brightness).get()
|
||||||
|
});
|
||||||
|
|
||||||
//$scope.categories.length = 0;
|
revenues.push({
|
||||||
//$scope.categories.concat(categories);
|
name: category.category,
|
||||||
$scope.categories = categories;
|
y: category.revenues,
|
||||||
|
color: Highcharts.Color(config.series[0].data[0].color).brighten(brightness).get()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
nv.addGraph($scope.drawChart);
|
// Note: expenses and revenues must be in the same order than in series[0].
|
||||||
nv.addGraph($scope.drawPieChart);
|
config.series[1].data = revenues.concat(expenses);
|
||||||
//$scope.drawPieChart(pieChartValues, "#expense-categories-chart-placeholder");
|
});
|
||||||
//$scope.drawPieChart();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.getAccountStatus = function(account, month) {
|
$scope.getAccountStatus = function(account, month) {
|
||||||
if(account != null && month != null) {
|
// Note: Month is 0 indexed.
|
||||||
// Note: Month is 0 indexed.
|
$scope.begin = moment({year: month.year, month: month.month - 1, day: 1});
|
||||||
var begin = moment({year: month.year, month: month.month - 1, day: 1});
|
$scope.end = $scope.begin.clone().endOf('month');
|
||||||
var end = begin.clone().endOf('month');
|
|
||||||
|
|
||||||
$http.get("/api/accounts/" + account.id,
|
$http.get("/api/accounts/" + account.id, {
|
||||||
{params: {
|
params: {
|
||||||
begin: begin.format('YYYY-MM-DD'),
|
begin: $scope.begin.format('YYYY-MM-DD'),
|
||||||
end: end.format('YYYY-MM-DD')
|
end: $scope.end.format('YYYY-MM-DD')
|
||||||
}}).success($scope.getAccountStatus_success);
|
}
|
||||||
}
|
}).success(function(status) {
|
||||||
};
|
var colors = Highcharts.getOptions().colors;
|
||||||
|
|
||||||
$scope.getAccountStatus_success = function(status) {
|
var config = $scope.categoriesChartConfig;
|
||||||
$scope.accountStatus = status;
|
|
||||||
|
// 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.
|
// Function to load entries from server for a specific account and month.
|
||||||
$scope.loadEntries = function(account, month) {
|
$scope.loadEntries = function(account, month) {
|
||||||
if(account) {
|
if(account) {
|
||||||
$scope.account = account;
|
$scope.account = account;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up selected entry.
|
// Clean up selected entry.
|
||||||
$scope.selectedItem = null;
|
$scope.selectedItem = null;
|
||||||
$scope.savedItem = null;
|
$scope.savedItem = null;
|
||||||
|
|
||||||
if(account && month) {
|
if(account && month) {
|
||||||
// Note: Month is 0 indexed.
|
// Note: Month is 0 indexed.
|
||||||
var begin = moment({year: month.year, month: month.month - 1, day: 1});
|
var begin = moment({year: month.year, month: month.month - 1, day: 1});
|
||||||
var end = begin.clone().endOf('month');
|
var end = begin.clone().endOf('month');
|
||||||
|
|
||||||
$http.get("/api/entries",
|
$http.get("/api/entries", {
|
||||||
{params: {
|
params: {
|
||||||
account: account.id,
|
account: account.id,
|
||||||
begin: begin.format('YYYY-MM-DD'),
|
begin: begin.format('YYYY-MM-DD'),
|
||||||
end: end.format('YYYY-MM-DD')
|
end: end.format('YYYY-MM-DD')
|
||||||
}}).success($scope.loadEntries_success);
|
}
|
||||||
} else {
|
}).success($scope.loadEntries_success);
|
||||||
$scope.loadEntries_success(null);
|
} else {
|
||||||
}
|
$scope.loadEntries_success(null);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load entries success callback
|
// 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){
|
$rootScope.$on("monthsLoadedEvent", function(event, args){
|
||||||
$scope.loadEntries(args.account, args.month);
|
$scope.loadEntries(args.account, args.month);
|
||||||
});
|
});
|
||||||
@ -460,8 +415,4 @@ accountantApp.controller(
|
|||||||
$rootScope.$on("monthsLoadedEvent", function(event, args){
|
$rootScope.$on("monthsLoadedEvent", function(event, args){
|
||||||
$scope.getAccountStatus(args.account, args.month);
|
$scope.getAccountStatus(args.account, args.month);
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.$on("entriesLoadedEvent", function(event, args) {
|
|
||||||
$scope.entriesLoaded(args.entries);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -20,25 +20,10 @@
|
|||||||
<!-- Chart row -->
|
<!-- Chart row -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- Sold evolution chart placeholder -->
|
<!-- Sold evolution chart placeholder -->
|
||||||
<div class="col-md-7">
|
<highchart id="entries-chart" config="entriesChartConfig" class="col-md-8"></highchart>
|
||||||
<svg id="entries-chart-placeholder" style="stroke-width: 1px"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Expense category piechart -->
|
<!-- Expense category piechart -->
|
||||||
<div class="col-md-3">
|
<highchart id="expense-categories-chart" config="categoriesChartConfig" class="col-md-4"></highchart>
|
||||||
<svg id="expense-categories-chart-placeholder" style='height:300px'/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Balance -->
|
|
||||||
<div class="col-md-2">
|
|
||||||
<div class="row">
|
|
||||||
<table class="table">
|
|
||||||
<tr><td>Dépenses :</td><td>[[accountStatus.expenses]]</td></tr>
|
|
||||||
<tr><td>Recettes :</td><td>[[accountStatus.revenues]]</td></tr>
|
|
||||||
<tr><td>Balance :</td><td><span ng-class="entryValueClass(accountStatus.balance)">[[accountStatus.balance]]</span></td></tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Row with entry table -->
|
<!-- Row with entry table -->
|
||||||
|
Loading…
Reference in New Issue
Block a user