Style with jscs.

This commit is contained in:
Alexis Lahouze 2016-10-09 20:17:11 +02:00
parent 655d72d4ae
commit e5721b3573
6 changed files with 709 additions and 640 deletions

35
.jscsrc Normal file
View File

@ -0,0 +1,35 @@
{
"preset": "google",
"fileExtensions": [".js", "jscs"],
"requireSemicolons": true,
"requireParenthesesAroundIIFE": true,
"maximumLineLength": 120,
"validateLineBreaks": "LF",
"validateIndentation": 4,
"disallowTrailingComma": true,
"disallowUnusedParams": true,
"disallowSpacesInsideObjectBrackets": null,
"disallowImplicitTypeConversion": ["string"],
"safeContextKeyword": "_this",
"jsDoc": {
"checkAnnotations": "closurecompiler",
"checkParamNames": true,
"requireParamTypes": true,
"checkRedundantParams": true,
"checkReturnTypes": true,
"checkRedundantReturns": true,
"requireReturnTypes": true,
"checkTypes": "capitalizedNativeCase",
"checkRedundantAccess": true,
"requireNewlineAfterDescription": true
},
"excludeFiles": [
"test/data/**",
"patterns/*"
]
}

View File

@ -1,3 +1,4 @@
// vim: set tw=80 ts=4 sw=4 sts=4:
/* /*
This file is part of Accountant. This file is part of Accountant.
@ -14,7 +15,6 @@
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with Accountant. If not, see <http://www.gnu.org/licenses/>. along with Accountant. If not, see <http://www.gnu.org/licenses/>.
*/ */
// vim: set tw=80 ts=2 sw=2 sts=2:
'use strict'; 'use strict';
angular.module('accountant.accounts', [ angular.module('accountant.accounts', [
@ -66,92 +66,100 @@ angular.module('accountant.accounts', [
* overdraft. * overdraft.
*/ */
$scope.rowClass = function(account) { $scope.rowClass = function(account) {
if(!account || !account.authorized_overdraft || !account.current) { // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
return; if (!account || !account.authorized_overdraft || !account.current) {
} return;
}
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
if(account.current < account.authorized_overdraft) { // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
return 'danger'; if (account.current < account.authorized_overdraft) {
} else if(account.current < 0) { return 'danger';
return 'warning'; } else if (account.current < 0) {
} return 'warning';
}
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
}; };
/* /*
* Return the class for a value compared to account authorized overdraft. * Return the class for a value compared to account authorized overdraft.
*/ */
$scope.valueClass = function(account, value) { $scope.valueClass = function(account, value) {
if(!account || !value) { if (!account || !value) {
return; return;
} }
if(value < account.authorized_overdraft) { // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
if (value < account.authorized_overdraft) {
return 'text-danger'; return 'text-danger';
} else if(value < 0) { } else if (value < 0) {
return 'text-warning'; return 'text-warning';
} }
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
}; };
/* /*
* Add an empty account. * Add an empty account.
*/ */
$scope.add = function() { $scope.add = function() {
var account = new Account({ var account = new Account({
authorized_overdraft: 0 // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
}); authorized_overdraft: 0
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
});
// Insert account at the begining of the array. // Insert account at the begining of the array.
$scope.accounts.splice(0, 0, account); $scope.accounts.splice(0, 0, account);
}; };
/* /*
* Cancel account edition. Remove it from array if a new one. * Cancel account edition. Remove it from array if a new one.
*/ */
$scope.cancelEdit = function(rowform, account, $index) { $scope.cancelEdit = function(rowform, account, $index) {
if(!account.id) { if (!account.id) {
// Account not saved, just remove it from array. // Account not saved, just remove it from array.
$scope.accounts.splice($index, 1); $scope.accounts.splice($index, 1);
} else { } else {
rowform.$cancel(); rowform.$cancel();
} }
}; };
/* /*
* Save account. * Save account.
*/ */
$scope.save = function(account) { $scope.save = function(account) {
//var account = $scope.accounts[$index]; //var account = $scope.accounts[$index];
//account = angular.merge(account, $data); //account = angular.merge(account, $data);
return account.$save().then(function(data) { return account.$save().then(function(data) {
Notification.success('Account #' + data.id + ' saved.'); Notification.success('Account #' + data.id + ' saved.');
// TODO Alexis Lahouze 2016-03-08 Update solds // TODO Alexis Lahouze 2016-03-08 Update solds
return data; return data;
}); });
}; };
/* /*
* Delete an account. * Delete an account.
*/ */
$scope.delete = function(account, $index) { $scope.delete = function(account, $index) {
var id = account.id; var id = account.id;
$ngBootbox.confirm( $ngBootbox.confirm(
'Voulez-vous supprimer le compte \'' + account.name + '\' ?', 'Voulez-vous supprimer le compte \'' + account.name + '\' ?',
function(result) { function(result) {
if(result) { if (result) {
account.$delete().then(function() { account.$delete().then(function() {
Notification.success('Account #' + id + ' deleted.'); Notification.success('Account #' + id + ' deleted.');
// Remove account from array. // Remove account from array.
$scope.accounts.splice($index, 1); $scope.accounts.splice($index, 1);
}); });
} }
} }
); );
}; };
// Load accounts. // Load accounts.
@ -169,46 +177,50 @@ angular.module('accountant.accounts', [
link: function(scope, element) { link: function(scope, element) {
var title = 'Account'; var title = 'Account';
if(scope.account && scope.account.id) { if (scope.account && scope.account.id) {
title = title + ' #' + scope.account.id; title = title + ' #' + scope.account.id;
} }
scope.form = {}; scope.form = {};
scope.submitForm = function() { scope.submitForm = function() {
// check to make sure the form is completely valid // check to make sure the form is completely valid
if (!scope.form.$valid) { if (!scope.form.$valid) {
return false;
}
// Authorized overdraft is a positive integer but data is a negative integer.
scope.data.authorized_overdraft = -scope.data.authorized_overdraft
angular.copy(scope.data, scope.account);
// Save account
console.log(scope.account);
return scope.account.$save().then(
function(data) {
Notification.success('Account #' + data.id + ' saved.');
scope.account.getSolds();
return data;
}, function(data) {
Notification.error('Error while saving account #' + data.id);
scope.account.getSolds();
console.log(data);
return false; return false;
} }
);
// Authorized overdraft is a positive integer but data is a negative integer.
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
scope.data.authorized_overdraft = -scope.data.authorized_overdraft;
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
angular.copy(scope.data, scope.account);
// Save account
console.log(scope.account);
return scope.account.$save().then(
function(data) {
Notification.success('Account #' + data.id + ' saved.');
scope.account.getSolds();
return data;
}, function(data) {
Notification.error('Error while saving account #' + data.id);
scope.account.getSolds();
console.log(data);
return false;
}
);
}; };
element.on('click', function() { element.on('click', function() {
// Create new account if not passed in ng-model. // Create new account if not passed in ng-model.
if(!scope.account) { if (!scope.account) {
scope.account = new Account({ scope.account = new Account({
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
authorized_overdraft: 0 authorized_overdraft: 0
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
}); });
} }
@ -216,7 +228,9 @@ angular.module('accountant.accounts', [
angular.copy(scope.account, scope.data); angular.copy(scope.account, scope.data);
// Authorized overdraft must be positive in form. // Authorized overdraft must be positive in form.
scope.data.authorized_overdraft = -scope.data.authorized_overdraft // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
scope.data.authorized_overdraft = -scope.data.authorized_overdraft;
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
// Open dialog with form. // Open dialog with form.
$ngBootbox.customDialog({ $ngBootbox.customDialog({
@ -240,4 +254,5 @@ angular.module('accountant.accounts', [
}); });
} }
}; };
}); }
);

View File

@ -1,3 +1,4 @@
// vim: set tw=80 ts=4 sw=4 sts=4:
/* /*
This file is part of Accountant. This file is part of Accountant.
@ -14,7 +15,6 @@
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with Accountant. If not, see <http://www.gnu.org/licenses/>. along with Accountant. If not, see <http://www.gnu.org/licenses/>.
*/ */
// vim: set tw=80 ts=2 sw=2 sts=2:
'use strict'; 'use strict';
angular.module('accountant', [ angular.module('accountant', [
@ -29,12 +29,12 @@ angular.module('accountant', [
.factory('sessionInjector', ['$storage', function($storage) { .factory('sessionInjector', ['$storage', function($storage) {
var sessionInjector = { var sessionInjector = {
request : function(config) { request: function(config) {
var token = $storage.get('token'); var token = $storage.get('token');
if(token) { if (token) {
var token_type = $storage.get('token_type'); var tokenType = $storage.get('token_type');
var authorization = token_type + ' ' + token; var authorization = tokenType + ' ' + token;
config.headers.Authorization = authorization; config.headers.Authorization = authorization;
} }
@ -53,30 +53,29 @@ angular.module('accountant', [
.config(['$routeProvider', function($routeProvider) { .config(['$routeProvider', function($routeProvider) {
// Defining template and controller in function of route. // Defining template and controller in function of route.
$routeProvider $routeProvider
.when('/account/:accountId/operations', { .when('/account/:accountId/operations', {
templateUrl: 'views/operations.html', templateUrl: 'views/operations.html',
controller: 'OperationController', controller: 'OperationController',
controllerAs: 'operationsCtrl' controllerAs: 'operationsCtrl'
}) })
.when('/account/:accountId/scheduler', { .when('/account/:accountId/scheduler', {
templateUrl: 'views/scheduler.html', templateUrl: 'views/scheduler.html',
controller: 'SchedulerController', controller: 'SchedulerController',
controllerAs: 'schedulerCtrl' controllerAs: 'schedulerCtrl'
}) })
.when('/accounts', { .when('/accounts', {
templateUrl: 'views/accounts.html', templateUrl: 'views/accounts.html',
controller: 'AccountController', controller: 'AccountController',
controllerAs: 'accountsCtrl' controllerAs: 'accountsCtrl'
}) })
.otherwise({ .otherwise({
redirectTo: '/accounts' redirectTo: '/accounts'
}); });
}]) }])
.config(['$storageProvider', function($storageProvider) { .config(['$storageProvider', function($storageProvider) {
// Configure storage // Configure storage
// Set global prefix for stored keys // Set global prefix for stored keys
$storageProvider.setPrefix('accountant'); $storageProvider.setPrefix('accountant');
// Change the default storage engine // Change the default storage engine
@ -98,54 +97,58 @@ angular.module('accountant', [
$scope.dialogShown = false; $scope.dialogShown = false;
$scope.showLoginForm = function() { $scope.showLoginForm = function() {
// First, if there are registered credentials, use them // First, if there are registered credentials, use them
if($scope.dialogShown) { if ($scope.dialogShown) {
return; return;
}
$scope.dialogShown = true;
$storage.clear();
$ngBootbox.customDialog({
title: 'Authentification requise',
templateUrl: 'views/login.tmpl.html',
buttons: {
login: {
label: 'Login',
className: 'btn-primary',
callback: function() {
$scope.dialogShown = false;
var email = $('#email').val();
var password = $('#password').val();
$http.post(
'/api/user/login',
{
'email': email,
'password': password
}
).success(function(result) {
// TODO Alexis Lahouze 2015-08-28 Handle callback.
// Call to /api/login to retrieve the token
$storage.set('token_type', result.token_type);
$storage.set('token', result.token);
$storage.set('expiration_date', result.expiration_date);
authService.loginConfirmed();
});
}
},
cancel: {
label: 'Annuler',
className: 'btn-default',
callback: function() {
authService.loginCancelled(null, 'Login cancelled by user action.');
$scope.dialogShown = false;
}
}
} }
});
$scope.dialogShown = true;
$storage.clear();
$ngBootbox.customDialog({
title: 'Authentification requise',
templateUrl: 'views/login.tmpl.html',
buttons: {
login: {
label: 'Login',
className: 'btn-primary',
callback: function() {
$scope.dialogShown = false;
var email = $('#email').val();
var password = $('#password').val();
$http.post(
'/api/user/login',
{
'email': email,
'password': password
}
).success(function(result) {
// TODO Alexis Lahouze 2015-08-28 Handle callback.
// Call to /api/login to retrieve the token
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
$storage.set('token_type', result.token_type);
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
$storage.set('token', result.token);
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
$storage.set('expiration_date', result.expiration_date);
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
authService.loginConfirmed();
});
}
},
cancel: {
label: 'Annuler',
className: 'btn-default',
callback: function() {
authService.loginCancelled(null, 'Login cancelled by user action.');
$scope.dialogShown = false;
}
}
}
});
}; };
$rootScope.$on('event:auth-loginRequired', $scope.showLoginForm); $rootScope.$on('event:auth-loginRequired', $scope.showLoginForm);

View File

@ -1,3 +1,4 @@
// vim: set tw=80 ts=4 sw=4 sts=4:
/* /*
This file is part of Accountant. This file is part of Accountant.
@ -14,7 +15,6 @@
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with Accountant. If not, see <http://www.gnu.org/licenses/>. along with Accountant. If not, see <http://www.gnu.org/licenses/>.
*/ */
// vim: set tw=80 ts=2 sw=2 sts=2:
'use strict'; 'use strict';
angular.module('accountant.operations', [ angular.module('accountant.operations', [
@ -24,7 +24,7 @@ angular.module('accountant.operations', [
'ngBootbox', 'ngBootbox',
'ui-notification', 'ui-notification',
'mgcrea.ngStrap', 'mgcrea.ngStrap',
'highcharts-ng', 'highcharts-ng'
]) ])
.config(['$resourceProvider', function($resourceProvider) { .config(['$resourceProvider', function($resourceProvider) {
@ -32,7 +32,7 @@ angular.module('accountant.operations', [
$resourceProvider.defaults.stripTrailingSlashes = false; $resourceProvider.defaults.stripTrailingSlashes = false;
}]) }])
.factory('Operation', [ '$resource', function($resource) { .factory('Operation', ['$resource', function($resource) {
return $resource( return $resource(
'/api/operation/:id', { '/api/operation/:id', {
id: '@id' id: '@id'
@ -40,29 +40,35 @@ angular.module('accountant.operations', [
); );
}]) }])
.factory('OHLC', [ '$resource', '$routeParams', .factory('OHLC', ['$resource', '$routeParams',
function($resource, $routeParams) { function($resource, $routeParams) {
return $resource( return $resource(
'/api/account/:account_id/ohlc', { '/api/account/:account_id/ohlc', {
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
account_id: $routeParams.accountId account_id: $routeParams.accountId
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
} }
); );
}]) }])
.factory('Category', [ '$resource', '$routeParams', .factory('Category', ['$resource', '$routeParams',
function($resource, $routeParams) { function($resource, $routeParams) {
return $resource( return $resource(
'/api/account/:account_id/category', { '/api/account/:account_id/category', {
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
account_id: $routeParams.accountId account_id: $routeParams.accountId
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
} }
); );
}]) }])
.factory('Balance', [ '$resource', '$routeParams', .factory('Balance', ['$resource', '$routeParams',
function($resource, $routeParams) { function($resource, $routeParams) {
return $resource( return $resource(
'/api/account/:account_id/balance', { '/api/account/:account_id/balance', {
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
account_id: $routeParams.accountId account_id: $routeParams.accountId
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
} }
); );
}]) }])
@ -70,450 +76,457 @@ angular.module('accountant.operations', [
/* /*
* Controller for category chart. * Controller for category chart.
*/ */
.controller( .controller('CategoryChartController', [
'CategoryChartController', [
'$rootScope', '$scope', '$http', 'Category', 'Balance', '$rootScope', '$scope', '$http', 'Category', 'Balance',
function($rootScope, $scope, $http, Category, Balance) { function($rootScope, $scope, $http, Category, Balance) {
var colors = Highcharts.getOptions().colors;
$scope.revenueColor = colors[2];
$scope.expenseColor = colors[3];
var colors = Highcharts.getOptions().colors; // Configure pie chart for categories.
$scope.revenueColor = colors[2]; $scope.config = {
$scope.expenseColor = colors[3]; 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;
}
}
}]
};
// Configure pie chart for categories. $scope.brightenColor = function(color) {
$scope.config = { var brightness = 0.2;
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) { return Highcharts.Color(color).brighten(brightness).get();
var 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;
// Load categories, mainly to populate the pie chart. Category.query({
$scope.load = function(begin, end) { begin: begin.format('YYYY-MM-DD'),
$scope.config.loading = true; end: end.format('YYYY-MM-DD')
}, function(data) {
var expenses = [];
var revenues = [];
Category.query({ var expenseColor = $scope.brightenColor($scope.expenseColor);
begin: begin.format('YYYY-MM-DD'), var revenueColor = $scope.brightenColor($scope.revenueColor);
end: end.format('YYYY-MM-DD')
}, function(data) {
var expenses = [], revenues = [];
var expenseColor = $scope.brightenColor($scope.expenseColor); angular.forEach(angular.fromJson(data), function(category) {
var revenueColor = $scope.brightenColor($scope.revenueColor); expenses.push({
name: category.category,
y: -category.expenses,
color: expenseColor
});
angular.forEach(angular.fromJson(data), function(category) { revenues.push({
expenses.push({ name: category.category,
name: category.category, y: category.revenues,
y: -category.expenses, color: revenueColor
color: expenseColor });
});
// 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 balance.
*/
$scope.getBalance = function(begin, end) {
Balance.get({
begin: begin.format('YYYY-MM-DD'),
end: end.format('YYYY-MM-DD')
}, function(balance) {
// Update pie chart subtitle with Balance.
$scope.config.subtitle = {
text: 'Balance: ' + balance.balance
};
$scope.config.series[0].data = [{
name: 'Revenues',
y: balance.revenues,
color: $scope.revenueColor
}, {
name: 'Expenses',
y: -balance.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.getBalance(args.begin, args.end);
}); });
}
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 balance.
*/
$scope.getBalance = function(begin, end) {
Balance.get({
begin: begin.format('YYYY-MM-DD'),
end: end.format('YYYY-MM-DD')
}, function(balance) {
// Update pie chart subtitle with Balance.
$scope.config.subtitle = {
text: 'Balance: ' + balance.balance
};
$scope.config.series[0].data = [{
name: 'Revenues',
y: balance.revenues,
color: $scope.revenueColor
}, {
name: 'Expenses',
y: -balance.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.getBalance(args.begin, args.end);
});
}])
/* /*
* Controller for the sold chart. * Controller for the sold chart.
*/ */
.controller( .controller('SoldChartController', [
'SoldChartController', [
'$rootScope', '$scope', '$http', 'OHLC', '$rootScope', '$scope', '$http', 'OHLC',
function($rootScope, $scope, $http, OHLC) { function($rootScope, $scope, $http, OHLC) {
// Configure chart for operations. // Configure chart for operations.
$scope.config = { $scope.config = {
options: { options: {
chart: { chart: {
zoomType: 'x' zoomType: 'x'
}, },
rangeSelector: { rangeSelector: {
buttons: [{ buttons: [{
type: 'month', type: 'month',
count: 1, count: 1,
text: '1m' text: '1m'
}, { }, {
type: 'month', type: 'month',
count: 3, count: 3,
text: '3m' text: '3m'
}, { }, {
type: 'month', type: 'month',
count: 6, count: 6,
text: '6m' text: '6m'
}, { }, {
type: 'year', type: 'year',
count: 1, count: 1,
text: '1y' text: '1y'
}, { }, {
type: 'all', type: 'all',
text: 'All' text: 'All'
}], }],
selected: 0, selected: 0
}, },
navigator: { navigator: {
enabled: true enabled: true
}, },
tooltip: { tooltip: {
crosshairs: true, crosshairs: true,
shared: true, shared: true,
valueDecimals: 2, valueDecimals: 2,
valueSuffix: '€' valueSuffix: '€'
}, },
scrollbar: { scrollbar: {
liveRedraw: false liveRedraw: false
} }
}, },
series: [{ series: [{
type: 'ohlc', type: 'ohlc',
name: 'Sold', name: 'Sold',
data: [], data: [],
dataGrouping : { dataGrouping: {
units : [[ units: [[
'week', // unit name 'week', // unit name
[1] // allowed multiples [1] // allowed multiples
], [ ], [
'month', 'month',
[1, 2, 3, 4, 6] [1, 2, 3, 4, 6]
]] ]]
} }
}], }],
title: { title: {
text: 'Sold evolution' text: 'Sold evolution'
}, },
xAxis: { xAxis: {
type: 'datetime', type: 'datetime',
dateTimeLabelFormats: { dateTimeLabelFormats: {
month: '%e. %b', month: '%e. %b',
year: '%Y' year: '%Y'
}, },
minRange: 3600 * 1000 * 24 * 14, // 2 weeks minRange: 3600 * 1000 * 24 * 14, // 2 weeks
events: { events: {
afterSetExtremes: function(e) { afterSetExtremes: function(e) {
$scope.$emit('rangeSelectedEvent', { $scope.$emit('rangeSelectedEvent', {
begin: moment.utc(e.min), end: moment.utc(e.max) begin: moment.utc(e.min), end: moment.utc(e.max)
}); });
} }
}, },
currentMin: moment.utc().startOf('month'), currentMin: moment.utc().startOf('month'),
currentMax: moment.utc().endOf('month') currentMax: moment.utc().endOf('month')
}, },
yAxis: { yAxis: {
plotLines: [{ plotLines: [{
color: 'orange', color: 'orange',
width: 2, width: 2,
value: 0.0 value: 0.0
}, { }, {
color: 'red', color: 'red',
width: 2, width: 2,
value: 0.0 value: 0.0
}] }]
}, },
useHighStocks: true useHighStocks: true
}; };
$scope.loadSolds = function() { $scope.loadSolds = function() {
$scope.config.loading = true; $scope.config.loading = true;
OHLC.query({}, function(data) { OHLC.query({}, function(data) {
$scope.config.series[0].data = []; $scope.config.series[0].data = [];
angular.forEach(data, function(operation) { angular.forEach(data, function(operation) {
$scope.config.series[0].data.push([ $scope.config.series[0].data.push([
moment.utc(operation.operation_date).valueOf(), // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
operation.open, operation.high, operation.low, operation.close moment.utc(operation.operation_date).valueOf(),
]); // jscs:enable requireCamelCaseOrUpperCaseIdentifiers
}); operation.open, operation.high, operation.low, operation.close
]);
});
$scope.$emit('rangeSelectedEvent', { $scope.$emit('rangeSelectedEvent', {
begin: $scope.config.xAxis.currentMin, begin: $scope.config.xAxis.currentMin,
end: $scope.config.xAxis.currentMax end: $scope.config.xAxis.currentMax
}); });
$scope.config.loading = false; $scope.config.loading = false;
}); });
}; };
// Reload solds when an operation is saved. // Reload solds when an operation is saved.
$rootScope.$on('operationSavedEvent', function() { $rootScope.$on('operationSavedEvent', function() {
$scope.loadSolds(); $scope.loadSolds();
}); });
// Reload solds when an operation is deleted. // Reload solds when an operation is deleted.
$rootScope.$on('operationDeletedEvent', function() { $rootScope.$on('operationDeletedEvent', function() {
$scope.loadSolds(); $scope.loadSolds();
}); });
// Update authorized overdraft on account loading. // Update authorized overdraft on account loading.
$rootScope.$on('accountLoadedEvent', function(e, account) { $rootScope.$on('accountLoadedEvent', function(e, account) {
$scope.config.yAxis.plotLines[1].value = account.authorized_overdraft; // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
}); $scope.config.yAxis.plotLines[1].value = account.authorized_overdraft;
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
});
// Select beginning and end of month. // Select beginning and end of month.
$scope.loadSolds(); $scope.loadSolds();
}]) }
])
/* /*
* Controller for the operations. * Controller for the operations.
*/ */
.controller( .controller('OperationController', [
'OperationController', [ '$scope', '$rootScope', '$routeParams', '$ngBootbox', 'Notification', 'Account', 'Operation',
'$scope', '$rootScope', '$routeParams', '$ngBootbox', 'Notification', 'Account', 'Operation', function($scope, $rootScope, $routeParams, $ngBootbox, Notification, Account, Operation) {
function($scope, $rootScope, $routeParams, $ngBootbox, Notification, Account, Operation) { // List of operations.
// List of operations. $scope.operations = [];
$scope.operations = [];
/* /*
* Add an empty operation. * Add an empty operation.
*/ */
$scope.add = function() { $scope.add = function() {
var operation = new Operation({ var operation = new Operation({
account_id: $routeParams.accountId // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
}); account_id: $routeParams.accountId
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
});
$scope.operations.splice(0, 0, operation); $scope.operations.splice(0, 0, operation);
}; };
/* /*
* Load operations. * Load operations.
*/ */
$scope.load = function(begin, end) { $scope.load = function(begin, end) {
$scope.operations = Operation.query({ $scope.operations = Operation.query({
account_id: $routeParams.accountId, // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
begin: begin.format('YYYY-MM-DD'), account_id: $routeParams.accountId,
end: end.format('YYYY-MM-DD') // jscs:enable requireCamelCaseOrUpperCaseIdentifiers
}); begin: begin.format('YYYY-MM-DD'),
}; end: end.format('YYYY-MM-DD')
});
};
/* /*
* Cancel edition. * Cancel edition.
*/ */
$scope.cancelEdit = function(operation, rowform, $index) { $scope.cancelEdit = function(operation, rowform, $index) {
if(!operation.id) { if (!operation.id) {
$scope.operations.splice($index, 1); $scope.operations.splice($index, 1);
} else { } else {
rowform.$cancel(); 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) {
Notification.success('Operation #' + data.id + ' saved.');
$scope.$emit('operationSavedEvent', data);
});
};
/*
* Delete an operation and emit operationDeletedEvent.
*/
$scope.delete = function(operation, $index) {
var id = operation.id;
$ngBootbox.confirm(
'Voulez-vous supprimer l\'opération \\\'' + operation.label + '\\\' ?',
function(result) {
if(result) {
operation.$delete().then(function() {
Notification.success('Operation #' + id + ' deleted.');
// Remove operation from array.
$scope.operation.splice($index, 1);
$scope.$emit('operationDeletedEvent', operation);
});
}
}
);
};
$scope.account = Account.get({
id: $routeParams.accountId
});
/*
* Reload operations on rangeSelectedEvent.
*/
$rootScope.$on('rangeSelectedEvent', function(e, args) {
$scope.load(args.begin, args.end);
});
}])
.directive(
'operationFormDialog', function($ngBootbox) {
return {
restrict: 'A',
scope: {
operation: '=ngModel'
},
link: function(scope, element) {
var title = 'Operation';
if(scope.operation && scope.operation.id) {
title = title + ' #' + scope.operation.id;
}
scope.form = {};
element.on('click', function() {
scope.data = {};
angular.copy(scope.operation, scope.data);
// Open dialog with form.
$ngBootbox.customDialog({
scope: scope,
title: title,
templateUrl: 'views/operation.form.tmpl.html',
onEscape: true,
buttons: {
save: {
label: 'Save',
className: 'btn-success',
callback: function() {
// Validate form
console.log(scope.form);
// Save operation
console.log(scope.operation);
// TODO Alexis Lahouze 2016-05-24 Save operation, handle return.
return false;
}
},
cancel: {
label: 'Cancel',
className: 'btn-default',
callback: true
}
}
});
});
} }
}; };
/*
* 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) {
Notification.success('Operation #' + data.id + ' saved.');
$scope.$emit('operationSavedEvent', data);
});
};
/*
* Delete an operation and emit operationDeletedEvent.
*/
$scope.delete = function(operation, $index) {
var id = operation.id;
$ngBootbox.confirm(
'Voulez-vous supprimer l\'opération \\\'' + operation.label + '\\\' ?',
function(result) {
if (result) {
operation.$delete().then(function() {
Notification.success('Operation #' + id + ' deleted.');
// Remove operation from array.
$scope.operation.splice($index, 1);
$scope.$emit('operationDeletedEvent', operation);
});
}
}
);
};
$scope.account = Account.get({
id: $routeParams.accountId
});
/*
* Reload operations on rangeSelectedEvent.
*/
$rootScope.$on('rangeSelectedEvent', function(e, args) {
$scope.load(args.begin, args.end);
});
}
])
.directive('operationFormDialog', function($ngBootbox) {
return {
restrict: 'A',
scope: {
operation: '=ngModel'
},
link: function(scope, element) {
var title = 'Operation';
if (scope.operation && scope.operation.id) {
title = title + ' #' + scope.operation.id;
}
scope.form = {};
element.on('click', function() {
scope.data = {};
angular.copy(scope.operation, scope.data);
// Open dialog with form.
$ngBootbox.customDialog({
scope: scope,
title: title,
templateUrl: 'views/operation.form.tmpl.html',
onEscape: true,
buttons: {
save: {
label: 'Save',
className: 'btn-success',
callback: function() {
// Validate form
console.log(scope.form);
// Save operation
console.log(scope.operation);
// TODO Alexis Lahouze 2016-05-24 Save operation, handle return.
return false;
}
},
cancel: {
label: 'Cancel',
className: 'btn-default',
callback: true
}
}
});
});
}
};
}); });

View File

@ -1,3 +1,4 @@
// vim: set tw=80 ts=4 sw=4 sts=4:
/* /*
This file is part of Accountant. This file is part of Accountant.
@ -14,7 +15,6 @@
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with Accountant. If not, see <http://www.gnu.org/licenses/>. along with Accountant. If not, see <http://www.gnu.org/licenses/>.
*/ */
// vim: set tw=80 ts=2 sw=2 sts=2:
'use strict'; 'use strict';
angular.module('accountant.scheduler', [ angular.module('accountant.scheduler', [
@ -38,84 +38,88 @@ angular.module('accountant.scheduler', [
); );
}]) }])
.controller( .controller('SchedulerController', [
'SchedulerController', [ '$scope', '$rootScope', '$routeParams', '$ngBootbox', 'Notification', 'ScheduledOperation',
'$scope', '$rootScope', '$routeParams', '$ngBootbox', 'Notification', 'ScheduledOperation', function($scope, $rootScope, $routeParams, $ngBootbox, Notification, ScheduledOperation) {
function($scope, $rootScope, $routeParams, $ngBootbox, Notification, ScheduledOperation) { // Operation store.
// Operation store. $scope.operations = [];
$scope.operations = [];
/* /*
* Add a new operation at the beginning of th array. * Add a new operation at the beginning of th array.
*/ */
$scope.add = function() { $scope.add = function() {
var operation = new ScheduledOperation({ var operation = new ScheduledOperation({
account_id: $routeParams.accountId // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
}); account_id: $routeParams.accountId
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
// Insert new operation at the beginning of the array.
$scope.operations.splice(0, 0, operation);
};
/*
* Load operations.
*/
$scope.load = function() {
$scope.operations = ScheduledOperation.query({
account_id: $routeParams.accountId
});
};
/*
* Save operation.
*/
$scope.save = function($data, $index) {
var operation;
if($data.$save) {
operation = $data;
} else {
operation = $scope.operations[$index];
operation = angular.merge(operation, $data);
}
return operation.$save().then(function(data) {
Notification.success('Operation #' + data.id + ' saved.');
});
};
/*
* Cancel operation edition. Delete if new.
*/
$scope.cancelEdit = function(operation, rowform, $index) {
if(!operation.id) {
$scope.operations.splice($index, 1);
} else {
rowform.$cancel();
}
};
/*
* Delete operation.
*/
$scope.delete = function(operation, $index) {
var id = operation.id;
$ngBootbox.confirm(
'Voulez-vous supprimer l\'operation planifiée \\\'' + operation.label + '\\\' ?',
function(result) {
if(result) {
operation.$delete().then(function() {
Notification.success('Operation #' + id + ' deleted.');
// Remove account from array.
$scope.operations.splice($index, 1);
}); });
}
}
);
};
// Load operations on controller initialization. // Insert new operation at the beginning of the array.
$scope.load(); $scope.operations.splice(0, 0, operation);
}]); };
/*
* Load operations.
*/
$scope.load = function() {
$scope.operations = ScheduledOperation.query({
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
account_id: $routeParams.accountId
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
});
};
/*
* Save operation.
*/
$scope.save = function($data, $index) {
var operation;
if ($data.$save) {
operation = $data;
} else {
operation = $scope.operations[$index];
operation = angular.merge(operation, $data);
}
return operation.$save().then(function(data) {
Notification.success('Operation #' + data.id + ' saved.');
});
};
/*
* Cancel operation edition. Delete if new.
*/
$scope.cancelEdit = function(operation, rowform, $index) {
if (!operation.id) {
$scope.operations.splice($index, 1);
} else {
rowform.$cancel();
}
};
/*
* Delete operation.
*/
$scope.delete = function(operation, $index) {
var id = operation.id;
$ngBootbox.confirm(
'Voulez-vous supprimer l\'operation planifiée \\\'' + operation.label + '\\\' ?',
function(result) {
if (result) {
operation.$delete().then(function() {
Notification.success('Operation #' + id + ' deleted.');
// Remove account from array.
$scope.operations.splice($index, 1);
});
}
}
);
};
// Load operations on controller initialization.
$scope.load();
}
]);

View File

@ -1,7 +1,6 @@
module.exports = { module.exports = {
options: { options: {
config: '.jscsrc', config: '.jscsrc'
verbose: true
}, },
frontend_js: [ frontend_js: [
'<%= accountant.frontend.src %>/js/*.js' '<%= accountant.frontend.src %>/js/*.js'