2015-08-21 01:25:48 +02:00

429 lines
10 KiB
JavaScript

/*
This file is part of Accountant.
Accountant is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Accountant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
*/
accountantApp
.factory("Operation", [ "$resource", function($resource) {
return $resource(
"/api/operations/:id", {
id: "@id"
}
);
}])
.controller(
"CategoryChartController", [
"$rootScope", "$scope", "$http", "$routeParams",
function($rootScope, $scope, $http, $routeParams) {
var colors = Highcharts.getOptions().colors;
$scope.revenueColor = colors[2];
$scope.expenseColor = colors[3];
// Configure pie chart for categories.
$scope.config = {
options: {
chart: {
type: 'pie',
animation: {
duration: 500
}
},
plotOptions: {
pie: {
startAngle: -90
},
series: {
allowPointSelect: 0
}
},
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.percentage >= 2.5 ? this.point.name : null;
},
}
}]
};
$scope.brightenColor = function(color) {
brightness = 0.2;
return Highcharts.Color(color).brighten(brightness).get();
};
// Load categories, mainly to populate the pie chart.
$scope.load = function(begin, end) {
$scope.config.loading = true;
$http.get("/api/categories", {
params: {
account: $routeParams.accountId,
begin: begin.format('YYYY-MM-DD'),
end: end.format('YYYY-MM-DD')
}
}).success(function(data) {
var expenses = [], revenues = [];
var expenseColor = $scope.brightenColor($scope.expenseColor);
var revenueColor = $scope.brightenColor($scope.revenueColor);
angular.forEach(angular.fromJson(data), function(category) {
expenses.push({
name: category.category,
y: -category.expenses,
color: expenseColor
});
revenues.push({
name: category.category,
y: category.revenues,
color: revenueColor
});
});
// Note: expenses and revenues must be in the same order than in series[0].
$scope.config.series[1].data = revenues.concat(expenses);
$scope.config.loading = false;
});
};
/*
* Get account status.
*/
$scope.getAccountStatus = function(begin, end) {
$http.get("/api/accounts/" + $routeParams.accountId, {
params: {
begin: begin.format('YYYY-MM-DD'),
end: end.format('YYYY-MM-DD')
}
}).success(function(account) {
// Emit accountLoadedEvent.
$scope.$emit("accountLoadedEvent", account);
// Update pie chart subtitle with Balance.
$scope.config.subtitle = {
text: "Balance: " + account.balance
};
$scope.config.series[0].data = [{
name: "Revenues",
y: account.revenues,
color: $scope.revenueColor
}, {
name: "Expenses",
y: -account.expenses,
color: $scope.expenseColor,
}];
});
};
// Reload categories and account status on range selection.
$rootScope.$on("rangeSelectedEvent", function(e, args) {
$scope.load(args.begin, args.end);
$scope.getAccountStatus(args.begin, args.end);
});
}])
.controller(
"SoldChartController", [
"$rootScope", "$scope", "$http", "$routeParams",
function($rootScope, $scope, $http, $routeParams) {
// Configure chart for operations.
$scope.config = {
options: {
chart: {
zoomType: 'x'
},
rangeSelector: {
buttons: [{
type: 'month',
count: 1,
text: "1m"
}, {
type: "month",
count: 3,
text: "3m"
}, {
type: "month",
count: 6,
text: "6m"
}, {
type: "year",
count: 1,
text: "1y"
}, {
type: "all",
text: "All"
}],
selected: 0,
},
navigator: {
enabled: true
},
tooltip: {
crosshairs: true,
shared: true,
valueDecimals: 2,
valueSuffix: '€'
},
scrollbar: {
liveRedraw: false
}
},
series: [{
type: "ohlc",
name: "Sold",
data: [],
dataGrouping : {
units : [[
'week', // unit name
[1] // allowed multiples
], [
'month',
[1, 2, 3, 4, 6]
]]
}
}],
title: {
text: "Sold evolution"
},
xAxis: {
type: "datetime",
dateTimeLabelFormats: {
month: "%e. %b",
year: "%Y"
},
minRange: 3600 * 1000 * 24 * 14, // 2 weeks
events: {
afterSetExtremes: function(e) {
$scope.$emit("rangeSelectedEvent", {
begin: moment.utc(e.min), end: moment.utc(e.max)
});
}
},
currentMin: moment.utc().startOf('month'),
currentMax: moment.utc().endOf('month')
},
yAxis: {
plotLines: [{
color: "orange",
width: 2,
value: 0.0
}, {
color: "red",
width: 2,
value: 0.0
}]
},
useHighStocks: true
};
$scope.loadSolds = function() {
$scope.config.loading = true;
$http.get("/api/solds", {
params: {
account: $routeParams.accountId,
}
}).success(function(data) {
$scope.config.series[0].data = [];
angular.forEach(data, function(operation) {
$scope.config.series[0].data.push([
moment.utc(operation.operation_date).valueOf(),
operation.open, operation.high, operation.low, operation.close
]);
});
$scope.$emit("rangeSelectedEvent", {
begin: $scope.config.xAxis.currentMin,
end: $scope.config.xAxis.currentMax
});
$scope.config.loading = false;
});
};
// Reload solds when an operation is saved.
$rootScope.$on("operationSavedEvent", function(e, operation) {
$scope.loadSolds();
});
// Reload solds when an operation is deleted.
$rootScope.$on("operationDeletedEvent", function(e, operation) {
$scope.loadSolds();
});
// Update authorized overdraft on account loading.
$rootScope.$on("accountLoadedEvent", function(e, account) {
$scope.config.yAxis.plotLines[1].value = account.authorized_overdraft;
});
// Select beginning and end of month.
$scope.loadSolds();
}])
.controller(
"OperationController", [
"$scope", "$http", "$rootScope", "$filter", "$routeParams", "notificationService", "Operation",
function($scope, $http, $rootScope, $filter, $routeParams, notificationService, Operation) {
// Operation store.
$scope.operations = [];
/*
* Add a new operation.
*/
$scope.add = function() {
var operation = new Operation({
account_id: $routeParams.accountId
});
$scope.operations.splice(0, 0, operation);
};
/*
* Load operations.
*/
$scope.load = function(begin, end) {
$scope.operations = Operation.query({
account: $routeParams.accountId,
begin: begin.format('YYYY-MM-DD'),
end: end.format('YYYY-MM-DD')
});
};
/*
* Cancel edition.
*/
$scope.cancelEdit = function(operation, rowform, $index) {
if(!operation.id) {
$scope.operations.splice($index, 1);
} else {
rowform.$cancel();
}
};
/*
* Toggle pointed indicator for an operation.
*/
$scope.togglePointed = function(operation, rowform) {
operation.pointed = !operation.pointed;
// Save operation if not editing it.
if(!rowform.$visible) {
$scope.save(operation);
}
};
/*
* Toggle cancel indicator for an operation.
*/
$scope.toggleCanceled = function(operation) {
operation.canceled = !operation.canceled;
$scope.save(operation);
};
/*
* Save an operation and emit operationSavedEvent.
*/
$scope.save = function($data, $index) {
// Check if $data is already a resource.
var operation;
if($data.$save) {
operation = $data;
} else {
operation = $scope.operations[$index];
operation = angular.merge(operation, $data);
}
operation.confirmed = true;
return operation.$save().then(function(data) {
notificationService.success("Operation #" + data.id + " saved.");
$scope.$emit("operationSavedEvent", data);
});
};
/*
* Delete an operation and emit operationDeletedEvent.
*/
$scope.delete = function(operation, $index) {
var id = operation.id;
bootbox.confirm(
"Voulez-vous supprimer l'opération \"" + operation.label + "\" ?",
function(result) {
if(result) {
operation.$delete().then(function() {
notificationService.success("Operation #" + id + " deleted.");
// Remove operation from array.
$scope.operation.splice($index, 1);
$scope.$emit("operationDeletedEvent", operation);
});
}
}
);
};
/*
* Save account in scope to colorize with authorized overdraft.
*/
$rootScope.$on("accountLoadedEvent", function(e, account) {
$scope.account = account;
});
/*
* Reload operations on rangeSelectedEvent.
*/
$rootScope.$on("rangeSelectedEvent", function(e, args) {
$scope.load(args.begin, args.end);
});
}]);