Move frontend in separate directory.
parent
8357d4befa
commit
1917fe909d
@ -1,7 +0,0 @@
|
|||||||
.italic {
|
|
||||||
font-style: italic
|
|
||||||
}
|
|
||||||
|
|
||||||
.stroke {
|
|
||||||
text-decoration: line-through
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
<!--
|
|
||||||
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/>.
|
|
||||||
-->
|
|
||||||
<!-- vim: set tw=80 ts=2 sw=2 sts=2: -->
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="fr">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
|
|
||||||
<!-- Title -->
|
|
||||||
<title>Accountant</title>
|
|
||||||
|
|
||||||
<!-- build:css(accountant/frontend/static) css/vendor.css -->
|
|
||||||
<!-- bower:css -->
|
|
||||||
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" />
|
|
||||||
<link rel="stylesheet" href="bower_components/bootstrap-additions/dist/bootstrap-additions.css" />
|
|
||||||
<link rel="stylesheet" href="bower_components/angular-xeditable/dist/css/xeditable.css" />
|
|
||||||
<link rel="stylesheet" href="bower_components/angular-ui-notification/dist/angular-ui-notification.css" />
|
|
||||||
<link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.css" />
|
|
||||||
<!-- endbower -->
|
|
||||||
<!-- endbuild -->
|
|
||||||
|
|
||||||
<!-- Custom styles -->
|
|
||||||
<!-- build:css(.tmp) css/main.css -->
|
|
||||||
<!-- include: "type": "css", "files": "css/*.css" -->
|
|
||||||
<link href="css/main.css" rel="stylesheet" type="text/css">
|
|
||||||
<!-- /include -->
|
|
||||||
<!-- endbuild -->
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<!-- htmllint attr-bans="false" -->
|
|
||||||
<body style="padding-bottom: 50px; padding-top: 70px" ng-app="accountant">
|
|
||||||
<!-- htmllint attr-bans="$previous" -->
|
|
||||||
<!-- Navbar -->
|
|
||||||
<nav class="navbar navbar-fixed-top navbar-inverse">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<!-- Brand -->
|
|
||||||
<div class="navbar-header">
|
|
||||||
<a class="navbar-brand" href="#/accounts"> Accountant</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="container-fluid" ng-controller="MainController">
|
|
||||||
<div ng-view></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- build:js(accountant/frontend/static) js/vendor.js -->
|
|
||||||
<!-- bower:js -->
|
|
||||||
<script src="bower_components/jquery/dist/jquery.js"></script>
|
|
||||||
<script src="bower_components/moment/moment.js"></script>
|
|
||||||
<script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
|
|
||||||
<script src="bower_components/angular/angular.js"></script>
|
|
||||||
<script src="bower_components/angular-resource/angular-resource.js"></script>
|
|
||||||
<script src="bower_components/angular-route/angular-route.js"></script>
|
|
||||||
<script src="bower_components/angular-strap/dist/angular-strap.js"></script>
|
|
||||||
<script src="bower_components/angular-strap/dist/angular-strap.tpl.js"></script>
|
|
||||||
<script src="bower_components/angular-xeditable/dist/js/xeditable.js"></script>
|
|
||||||
<script src="bower_components/angular-ui-notification/dist/angular-ui-notification.js"></script>
|
|
||||||
<script src="bower_components/highcharts-ng/dist/highcharts-ng.js"></script>
|
|
||||||
<script src="bower_components/highstock-release/highstock.js"></script>
|
|
||||||
<script src="bower_components/highstock-release/highcharts-more.js"></script>
|
|
||||||
<script src="bower_components/highstock-release/modules/exporting.js"></script>
|
|
||||||
<script src="bower_components/angular-http-auth/src/http-auth-interceptor.js"></script>
|
|
||||||
<script src="bower_components/meanie-angular-storage/release/meanie-angular-storage.js"></script>
|
|
||||||
<script src="bower_components/bootbox/bootbox.js"></script>
|
|
||||||
<script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
|
|
||||||
<script src="bower_components/ngBootbox/dist/ngBootbox.js"></script>
|
|
||||||
<!-- endbower -->
|
|
||||||
<!-- endbuild -->
|
|
||||||
|
|
||||||
<!-- Custom Javascript libraries -->
|
|
||||||
<!-- build:js({.tmp,accountant/frontend/static}) js/scripts.js -->
|
|
||||||
<!-- include: "type": "js", "files": "js/*.js" -->
|
|
||||||
<script src="js/accounts.js"></script>
|
|
||||||
<script src="js/app.js"></script>
|
|
||||||
<script src="js/operations.js"></script>
|
|
||||||
<script src="js/scheduler.js"></script>
|
|
||||||
<!-- /include -->
|
|
||||||
<!-- endbuild -->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,159 +0,0 @@
|
|||||||
/*
|
|
||||||
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/>.
|
|
||||||
*/
|
|
||||||
// vim: set tw=80 ts=2 sw=2 sts=2:
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
angular.module('accountant.accounts', [
|
|
||||||
'ngResource',
|
|
||||||
'ui-notification',
|
|
||||||
'xeditable',
|
|
||||||
'ngBootbox'
|
|
||||||
])
|
|
||||||
|
|
||||||
.config(['$resourceProvider', function($resourceProvider) {
|
|
||||||
// Keep trailing slashes to avoid redirect by flask..
|
|
||||||
$resourceProvider.defaults.stripTrailingSlashes = false;
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('Account', ['$resource', function($resource) {
|
|
||||||
var Account = $resource(
|
|
||||||
'/api/account/:id', {
|
|
||||||
id: '@id'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
Account.prototype.getSolds = function() {
|
|
||||||
var Solds = $resource('/api/account/:id/solds', {id: this.id});
|
|
||||||
|
|
||||||
this.solds = Solds.get();
|
|
||||||
};
|
|
||||||
|
|
||||||
Account.prototype.getBalance = function(begin, end) {
|
|
||||||
var Balance = $resource(
|
|
||||||
'/api/account/:id/balance', {
|
|
||||||
id: this.id,
|
|
||||||
begin: begin.format('YYYY-MM-DD'),
|
|
||||||
end: end.format('YYYY-MM-DD')
|
|
||||||
});
|
|
||||||
|
|
||||||
this.balance = Balance.get();
|
|
||||||
};
|
|
||||||
|
|
||||||
return Account;
|
|
||||||
}])
|
|
||||||
|
|
||||||
.controller(
|
|
||||||
'AccountController', [
|
|
||||||
'$scope', '$ngBootbox', 'Account', 'Notification',
|
|
||||||
function($scope, $ngBootbox, Account, Notification) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the class for an account current value compared to authorized
|
|
||||||
* overdraft.
|
|
||||||
*/
|
|
||||||
$scope.rowClass = function(account) {
|
|
||||||
if(!account || !account.authorized_overdraft || !account.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(account.current < account.authorized_overdraft) {
|
|
||||||
return 'danger';
|
|
||||||
} else if(account.current < 0) {
|
|
||||||
return 'warning';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the class for a value compared to account authorized overdraft.
|
|
||||||
*/
|
|
||||||
$scope.valueClass = function(account, value) {
|
|
||||||
if(!account || !value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(value < account.authorized_overdraft) {
|
|
||||||
return 'text-danger';
|
|
||||||
} else if(value < 0) {
|
|
||||||
return 'text-warning';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add an empty account.
|
|
||||||
*/
|
|
||||||
$scope.add = function() {
|
|
||||||
var account = new Account({
|
|
||||||
authorized_overdraft: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
// Insert account at the begining of the array.
|
|
||||||
$scope.accounts.splice(0, 0, account);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Cancel account edition. Remove it from array if a new one.
|
|
||||||
*/
|
|
||||||
$scope.cancelEdit = function(rowform, account, $index) {
|
|
||||||
if(!account.id) {
|
|
||||||
// Account not saved, just remove it from array.
|
|
||||||
$scope.accounts.splice($index, 1);
|
|
||||||
} else {
|
|
||||||
rowform.$cancel();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Save account.
|
|
||||||
*/
|
|
||||||
$scope.save = function(account) {
|
|
||||||
//var account = $scope.accounts[$index];
|
|
||||||
|
|
||||||
//account = angular.merge(account, $data);
|
|
||||||
|
|
||||||
return account.$save().then(function(data) {
|
|
||||||
Notification.success('Account #' + data.id + ' saved.');
|
|
||||||
|
|
||||||
// TODO Alexis Lahouze 2016-03-08 Update solds
|
|
||||||
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Delete an account.
|
|
||||||
*/
|
|
||||||
$scope.delete = function(account, $index) {
|
|
||||||
var id = account.id;
|
|
||||||
|
|
||||||
$ngBootbox.confirm(
|
|
||||||
'Voulez-vous supprimer le compte \'' + account.name + '\' ?',
|
|
||||||
function(result) {
|
|
||||||
if(result) {
|
|
||||||
account.$delete().then(function() {
|
|
||||||
Notification.success('Account #' + id + ' deleted.');
|
|
||||||
|
|
||||||
// Remove account from array.
|
|
||||||
$scope.accounts.splice($index, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load accounts.
|
|
||||||
$scope.accounts = Account.query();
|
|
||||||
}]);
|
|
@ -1,154 +0,0 @@
|
|||||||
/*
|
|
||||||
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/>.
|
|
||||||
*/
|
|
||||||
// vim: set tw=80 ts=2 sw=2 sts=2:
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
angular.module('accountant', [
|
|
||||||
'accountant.accounts',
|
|
||||||
'accountant.operations',
|
|
||||||
'accountant.scheduler',
|
|
||||||
'ngRoute',
|
|
||||||
'ngBootbox',
|
|
||||||
'http-auth-interceptor',
|
|
||||||
'Storage.Service'
|
|
||||||
])
|
|
||||||
|
|
||||||
.factory('sessionInjector', ['$storage', function($storage) {
|
|
||||||
var sessionInjector = {
|
|
||||||
request : function(config) {
|
|
||||||
var token = $storage.get('token');
|
|
||||||
|
|
||||||
if(token) {
|
|
||||||
var token_type = $storage.get('token_type');
|
|
||||||
var authorization = token_type + ' ' + token;
|
|
||||||
config.headers.Authorization = authorization;
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return sessionInjector;
|
|
||||||
}])
|
|
||||||
|
|
||||||
.config(['$httpProvider', function($httpProvider) {
|
|
||||||
// Define interceptors.
|
|
||||||
$httpProvider.interceptors.push('sessionInjector');
|
|
||||||
}])
|
|
||||||
|
|
||||||
.config(['$routeProvider', function($routeProvider) {
|
|
||||||
// Defining template and controller in function of route.
|
|
||||||
$routeProvider
|
|
||||||
.when('/account/:accountId/operations', {
|
|
||||||
templateUrl: 'views/operations.html',
|
|
||||||
controller: 'OperationController',
|
|
||||||
controllerAs: 'operationsCtrl'
|
|
||||||
})
|
|
||||||
.when('/account/:accountId/scheduler', {
|
|
||||||
templateUrl: 'views/scheduler.html',
|
|
||||||
controller: 'SchedulerController',
|
|
||||||
controllerAs: 'schedulerCtrl'
|
|
||||||
})
|
|
||||||
.when('/accounts', {
|
|
||||||
templateUrl: 'views/accounts.html',
|
|
||||||
controller: 'AccountController',
|
|
||||||
controllerAs: 'accountsCtrl'
|
|
||||||
})
|
|
||||||
.otherwise({
|
|
||||||
redirectTo: '/accounts'
|
|
||||||
});
|
|
||||||
|
|
||||||
}])
|
|
||||||
|
|
||||||
.config(['$storageProvider', function($storageProvider) {
|
|
||||||
// Configure storage
|
|
||||||
// Set global prefix for stored keys
|
|
||||||
$storageProvider.setPrefix('accountant');
|
|
||||||
|
|
||||||
// Change the default storage engine
|
|
||||||
// Defaults to 'local'
|
|
||||||
$storageProvider.setDefaultStorageEngine('session');
|
|
||||||
|
|
||||||
// Change the enabled storage engines
|
|
||||||
// Defaults to ['memory', 'cookie', 'session', 'local']
|
|
||||||
$storageProvider.setEnabledStorageEngines(['local', 'session']);
|
|
||||||
}])
|
|
||||||
|
|
||||||
.run(function(editableOptions) {
|
|
||||||
editableOptions.theme = 'bs3'; // bootstrap3 theme. Can be also 'bs2', 'default'
|
|
||||||
})
|
|
||||||
|
|
||||||
.controller('MainController', [
|
|
||||||
'$scope', '$rootScope', '$http', 'authService', '$storage', '$ngBootbox',
|
|
||||||
function($scope, $rootScope, $http, authService, $storage, $ngBootbox) {
|
|
||||||
$scope.dialogShown = false;
|
|
||||||
|
|
||||||
$scope.showLoginForm = function() {
|
|
||||||
// First, if there are registered credentials, use them
|
|
||||||
if($scope.dialogShown) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$rootScope.$on('event:auth-loginRequired', $scope.showLoginForm);
|
|
||||||
}])
|
|
||||||
|
|
||||||
;
|
|
@ -1,466 +0,0 @@
|
|||||||
/*
|
|
||||||
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/>.
|
|
||||||
*/
|
|
||||||
// vim: set tw=80 ts=2 sw=2 sts=2:
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
angular.module('accountant.operations', [
|
|
||||||
'accountant.accounts',
|
|
||||||
'ngRoute',
|
|
||||||
'ngResource',
|
|
||||||
'ngBootbox',
|
|
||||||
'ui-notification',
|
|
||||||
'mgcrea.ngStrap',
|
|
||||||
'highcharts-ng',
|
|
||||||
])
|
|
||||||
|
|
||||||
.config(['$resourceProvider', function($resourceProvider) {
|
|
||||||
// Keep trailing slashes to avoid redirect by flask..
|
|
||||||
$resourceProvider.defaults.stripTrailingSlashes = false;
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('Operation', [ '$resource', function($resource) {
|
|
||||||
return $resource(
|
|
||||||
'/api/operation/:id', {
|
|
||||||
id: '@id'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('OHLC', [ '$resource', '$routeParams',
|
|
||||||
function($resource, $routeParams) {
|
|
||||||
return $resource(
|
|
||||||
'/api/account/:account_id/ohlc', {
|
|
||||||
account_id: $routeParams.accountId
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('Category', [ '$resource', '$routeParams',
|
|
||||||
function($resource, $routeParams) {
|
|
||||||
return $resource(
|
|
||||||
'/api/account/:account_id/category', {
|
|
||||||
account_id: $routeParams.accountId
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('Balance', [ '$resource', '$routeParams',
|
|
||||||
function($resource, $routeParams) {
|
|
||||||
return $resource(
|
|
||||||
'/api/account/:account_id/balance', {
|
|
||||||
account_id: $routeParams.accountId
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}])
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Controller for category chart.
|
|
||||||
*/
|
|
||||||
.controller(
|
|
||||||
'CategoryChartController', [
|
|
||||||
'$rootScope', '$scope', '$http', 'Category', 'Balance',
|
|
||||||
function($rootScope, $scope, $http, Category, Balance) {
|
|
||||||
|
|
||||||
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) {
|
|
||||||
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;
|
|
||||||
|
|
||||||
Category.query({
|
|
||||||
begin: begin.format('YYYY-MM-DD'),
|
|
||||||
end: end.format('YYYY-MM-DD')
|
|
||||||
}, 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 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(
|
|
||||||
'SoldChartController', [
|
|
||||||
'$rootScope', '$scope', '$http', 'OHLC',
|
|
||||||
function($rootScope, $scope, $http, OHLC) {
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
OHLC.query({}, 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() {
|
|
||||||
$scope.loadSolds();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Reload solds when an operation is deleted.
|
|
||||||
$rootScope.$on('operationDeletedEvent', function() {
|
|
||||||
$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 for the operations.
|
|
||||||
*/
|
|
||||||
.controller(
|
|
||||||
'OperationController', [
|
|
||||||
'$scope', '$rootScope', '$routeParams', '$ngBootbox', 'Notification', 'Account', 'Operation',
|
|
||||||
function($scope, $rootScope, $routeParams, $ngBootbox, Notification, Account, Operation) {
|
|
||||||
// List of operations.
|
|
||||||
$scope.operations = [];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add an empty 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_id: $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) {
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
}]);
|
|
@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
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/>.
|
|
||||||
*/
|
|
||||||
// vim: set tw=80 ts=2 sw=2 sts=2:
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
angular.module('accountant.scheduler', [
|
|
||||||
'ngRoute',
|
|
||||||
'ngBootbox',
|
|
||||||
'ui-notification',
|
|
||||||
'mgcrea.ngStrap'
|
|
||||||
])
|
|
||||||
|
|
||||||
.config(['$resourceProvider', function($resourceProvider) {
|
|
||||||
// Keep trailing slashes to avoid redirect by flask..
|
|
||||||
$resourceProvider.defaults.stripTrailingSlashes = false;
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('ScheduledOperation', ['$resource', function($resource) {
|
|
||||||
return $resource(
|
|
||||||
'/api/scheduled_operation/:id', {
|
|
||||||
id: '@id'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}])
|
|
||||||
|
|
||||||
.controller(
|
|
||||||
'SchedulerController', [
|
|
||||||
'$scope', '$rootScope', '$routeParams', '$ngBootbox', 'Notification', 'ScheduledOperation',
|
|
||||||
function($scope, $rootScope, $routeParams, $ngBootbox, Notification, ScheduledOperation) {
|
|
||||||
// Operation store.
|
|
||||||
$scope.operations = [];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add a new operation at the beginning of th array.
|
|
||||||
*/
|
|
||||||
$scope.add = function() {
|
|
||||||
var operation = new ScheduledOperation({
|
|
||||||
account_id: $routeParams.accountId
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
$scope.load();
|
|
||||||
}]);
|
|
@ -1,114 +0,0 @@
|
|||||||
<!--
|
|
||||||
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/>.
|
|
||||||
-->
|
|
||||||
<!-- vim: set tw=80 ts=2 sw=2 sts=2: -->
|
|
||||||
<div class="row">
|
|
||||||
<table class="table table-striped table-condensed table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Nom du compte</th>
|
|
||||||
<th class="col-md-1">Solde courant</th>
|
|
||||||
<th class="col-md-1">Solde pointé</th>
|
|
||||||
<th class="col-md-1">Découvert autorisé</th>
|
|
||||||
<th class="col-md-1">Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td colspan="5">
|
|
||||||
<button class="btn btn-success btn-success"
|
|
||||||
ng-click="add()">Ajouter</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr id="{{ account.id }}"
|
|
||||||
class="form-inline" ng-class="rowClass(account)"
|
|
||||||
ng-repeat="account in accounts | orderBy:'name'" ng-init="account.getSolds()">
|
|
||||||
<td>
|
|
||||||
<span editable-text="account.name"
|
|
||||||
e-placeholder="Nom du compte"
|
|
||||||
e-style="width: 100%"
|
|
||||||
e-name="name" e-form="rowform" e-required>
|
|
||||||
<a href="#/account/{{ account.id }}/operations">{{ account.name }}</a>
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<span ng-class="valueClass(account, account.solds.current)">
|
|
||||||
{{ account.solds.current | currency : "€" }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<span ng-class="valueClass(account, account.solds.pointed)">
|
|
||||||
{{ account.solds.pointed | currency : "€" }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<span editable-number="account.authorized_overdraft"
|
|
||||||
e-max="0"
|
|
||||||
e-style="width: 100%"
|
|
||||||
e-name="authorized_overdraft" e-form="rowform">
|
|
||||||
{{ account.authorized_overdraft | currency : "€" }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<form editable-form name="rowform"
|
|
||||||
onaftersave="save(account)" shown="!account.id">
|
|
||||||
<div class="btn-group btn-group-xs">
|
|
||||||
<!-- Edit account. -->
|
|
||||||
<button type="button" class="btn btn-success"
|
|
||||||
ng-if="!rowform.$visible"
|
|
||||||
ng-click="rowform.$show()">
|
|
||||||
<span class="fa fa-pencil-square-o"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Save account. -->
|
|
||||||
<button type="submit" class="btn btn-success"
|
|
||||||
ng-if="rowform.$visible">
|
|
||||||
<span class="fa fa-floppy-o"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Cancel account edition. -->
|
|
||||||
<button type="button" class="btn btn-default"
|
|
||||||
ng-if="rowform.$visible"
|
|
||||||
ng-click="cancelEdit(rowform, account, $index)">
|
|
||||||
<span class="fa fa-times"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Delete account, with confirm. -->
|
|
||||||
<button type="button" class="btn btn-default"
|
|
||||||
ng-if="account.id"
|
|
||||||
ng-click="delete(account, $index)">
|
|
||||||
<span class="fa fa-trash-o"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Open account scheduler. -->
|
|
||||||
<a class="btn btn-default"
|
|
||||||
ng-if="account.id"
|
|
||||||
href="#/account/{{ account.id }}/scheduler">
|
|
||||||
<span class="fa fa-clock-o"></span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
@ -1,17 +0,0 @@
|
|||||||
<!-- vim: set tw=80 ts=2 sw=2 sts=2: -->
|
|
||||||
<form class="form-horizontal">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="email" class="col-sm-4 control-label">Adresse email</label>
|
|
||||||
<div class="col-sm-8">
|
|
||||||
<input type="text" class="form-control" id="email" ng-model="email"
|
|
||||||
placeholder="Nom d'utilisateur">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="password" class="col-sm-4 control-label">Mot de passe</label>
|
|
||||||
<div class="col-sm-8">
|
|
||||||
<input type="password" class="form-control" id="password" ng-model="password" placeholder="Mot de passe">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
|||||||
<!--
|
|
||||||
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/>.
|
|
||||||
-->
|
|
||||||
<!-- vim: set tw=80 ts=2 sw=2 sts=2: -->
|
|
||||||
<div>
|
|
||||||
<!-- Chart row -->
|
|
||||||
<div class="row">
|
|
||||||
<!-- Sold evolution chart placeholder -->
|
|
||||||
<div class="col-md-8" ng-controller="SoldChartController">
|
|
||||||
<highchart id="sold-chart" config="config"></highchart>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Category piechart -->
|
|
||||||
<div class="col-md-4" ng-controller="CategoryChartController">
|
|
||||||
<highchart id="categories-chart" config="config"></highchart>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<table class="table table-striped table-condensed table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="col-md-1">Date d'op.</th>
|
|
||||||
<th>Libellé de l'opération</th>
|
|
||||||
<th class="col-md-1">Montant</th>
|
|
||||||
<th class="col-md-1">Solde</th>
|
|
||||||
<th class="col-md-2">Catégorie</th>
|
|
||||||
<th class="col-md-2">Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td colspan="6">
|
|
||||||
<button class="btn btn-success" ng-click="add()">
|
|
||||||
Ajouter
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr id="{{ operation.id }}" class="form-inline"
|
|
||||||
ng-class="{stroke: operation.canceled, italic: !operation.confirmed, warning: operation.sold < 0, danger: operation.sold < account.authorized_overdraft}"
|
|
||||||
ng-repeat="operation in operations | orderBy:['-operation_date', '-value', 'label']">
|
|
||||||
<td>
|
|
||||||
<span editable-text="operation.operation_date"
|
|
||||||
e-data-date-format="yyyy-MM-dd" e-bs-datepicker
|
|
||||||
e-timezone="UTC"
|
|
||||||
e-class="input-sm" e-style="width: 100%"
|
|
||||||
e-name="operation_date" e-form="rowform" e-required>
|
|
||||||
{{ operation.operation_date | date:"yyyy-MM-dd" }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<span editable-text="operation.label"
|
|
||||||
e-placeholder="Libellé de l'opération"
|
|
||||||
e-class="input-sm" e-style="width: 100%"
|
|
||||||
e-name="label" e-form="rowform" e-required>
|
|
||||||
{{ operation.label }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<span editable-number="operation.value"
|
|
||||||
e-class="input-sm" e-style="width: 100%"
|
|
||||||
e-name="value" e-form="rowform" e-required>
|
|
||||||
{{ operation.value | currency:"€" }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td ng-class="{'text-warning': operation.sold < 0, 'text-danger': operation.sold < account.authorized_overdraft}">
|
|
||||||
{{ operation.sold | currency:"€" }}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<span editable-text="operation.category"
|
|
||||||
e-placeholder="Catégorie"
|
|
||||||
e-class="input-sm" e-style="width: 100%"
|
|
||||||
e-name="category" e-form="rowform" e-required>
|
|
||||||
{{ operation.category }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<form editable-form name="rowform"
|
|
||||||
onbeforesave="save($data, $index)"
|
|
||||||
shown="!operation.id">
|
|
||||||
<div class="btn-group btn-group-xs">
|
|
||||||
<!-- Save current operation, for editing and non-confirmed non-canceled operation. -->
|
|
||||||
<button type="submit" class="btn btn-success"
|
|
||||||
ng-if="!operation.canceled && (!operation.confirmed || rowform.$visible)"
|
|
||||||
title="Save">
|
|
||||||
<span class="fa fa-floppy-o"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Edit operation, for non-canceled and non-editing operation -->
|
|
||||||
<button type="button" class="btn btn-default"
|
|
||||||
ng-if="!operation.canceled && !rowform.$visible"
|
|
||||||
ng-click="rowform.$show()" title="edit">
|
|
||||||
<span class="fa fa-pencil-square-o"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Cancel edition, for editing operation. -->
|
|
||||||
<button type="button" class="btn btn-default"
|
|
||||||
ng-if="rowform.$visible"
|
|
||||||
ng-click="cancelEdit(operation, rowform)">
|
|
||||||
<span class="fa fa-times"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Toggle pointed operation, for non-canceled operations. -->
|
|
||||||
<button type="button" class="btn btn-default"
|
|
||||||
ng-if="!operation.canceled"
|
|
||||||
ng-click="togglePointed(operation, rowform)"
|
|
||||||
ng-class="{active: operation.pointed}" title="point">
|
|
||||||
<span ng-class="{'fa fa-check-square-o': operation.pointed, 'fa fa-square-o': !operation.pointed}"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Toggle canceled operation, for non-editing operations. -->
|
|
||||||
<button type="button" class="btn btn-default"
|
|
||||||
ng-click="toggleCanceled(operation)"
|
|
||||||
ng-if="operation.scheduled_operation_id && !rowform.$visible"
|
|
||||||
ng-class="{active: operation.canceled}" title="cancel">
|
|
||||||
<span class="fa fa-remove"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Delete operation, with confirm. -->
|
|
||||||
<button type="button" class="btn btn-default"
|
|
||||||
ng-if="operation.id && !operation.scheduled_operation_id"
|
|
||||||
ng-click="delete(operation, $index)">
|
|
||||||
<span class="fa fa-trash-o"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,142 +0,0 @@
|
|||||||
<!--
|
|
||||||
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/>.
|
|
||||||
-->
|
|
||||||
<!-- vim: set tw=80 ts=2 sw=2 sts=2: -->
|
|
||||||
<div class="row">
|
|
||||||
<table class="table table-striped table-condensed table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="col-md-1">Date de début</th>
|
|
||||||
<th class="col-md-1">Date de fin</th>
|
|
||||||
<th class="col-md-1">Jour</th>
|
|
||||||
<th class="col-md-1">Fréq.</th>
|
|
||||||
<th>Libellé de l'opération</th>
|
|
||||||
<th class="col-md-1">Montant</th>
|
|
||||||
<th class="col-md-2">Catégorie</th>
|
|
||||||
<th class="col-md-1">Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td colspan="8">
|
|
||||||
<button class="btn btn-success" ng-click="add()">
|
|
||||||
Ajouter
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr id="{{ operation.id }}" class="form-inline"
|
|
||||||
ng-repeat="operation in operations">
|
|
||||||
<td class="col-md-1">
|
|
||||||
<span editable-text="operation.start_date"
|
|
||||||
e-style="width: 100%"
|
|
||||||
e-bs-datepicker e-data-date-format="yyyy-MM-dd"
|
|
||||||
e-name="start_date" e-form="rowform" e-required>
|
|
||||||
{{ operation.start_date | date: "yyyy-MM-dd" }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<span editable-text="operation.stop_date"
|
|
||||||
e-style="width: 100%"
|
|
||||||
e-bs-datepicker e-data-date-format="yyyy-MM-dd"
|
|
||||||
e-name="stop_date" e-form="rowform" e-required>
|
|
||||||
{{ operation.stop_date | date: "yyyy-MM-dd" }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<span editable-number="operation.day"
|
|
||||||
e-style="width: 100%"
|
|
||||||
e-name="day" e-form="rowform" e-required>
|
|
||||||
{{ operation.day }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<span editable-number="operation.frequency"
|
|
||||||
e-style="width: 100%"
|
|
||||||
e-name="frequency" e-form="rowform" e-required>
|
|
||||||
{{ operation.frequency }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<span editable-text="operation.label"
|
|
||||||
e-style="width: 100%"
|
|
||||||
e-placeholder="Libellé de l'opération"
|
|
||||||
e-name="label" e-form="rowform" e-required>
|
|
||||||
{{ operation.label }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<span editable-number="operation.value"
|
|
||||||
e-style="width: 100%"
|
|
||||||
e-name="value" e-form="rowform" e-required>
|
|
||||||
{{ operation.value | currency : "€" }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<span editable-text="operation.category"
|
|
||||||
e-style="width: 100%"
|
|
||||||
e-name="category" e-form="rowform">
|
|
||||||
{{ operation.category }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<form editable-form name="rowform"
|
|
||||||
onbeforesave="save($data, $index)"
|
|
||||||
shown="!operation.id">
|
|
||||||
<div class="btn-group btn-group-xs">
|
|
||||||
<!-- Save current operation -->
|
|
||||||
<button type="submit" class="btn btn-success"
|
|
||||||
ng-if="rowform.$visible" title="Save">
|
|
||||||
<span class="fa fa-floppy-o"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Edit operation. -->
|
|
||||||
<button type="button" class="btn btn-default"
|
|
||||||
ng-if="!rowform.$visible"
|
|
||||||
ng-click="rowform.$show()" title="edit">
|
|
||||||
<span class="fa fa-pencil-square-o"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Cancel edit. -->
|
|
||||||
<button type="button" class="btn btn-default"
|
|
||||||
ng-if="rowform.$visible"
|
|
||||||
ng-click="cancelEdit(operation, rowform, $index)"
|
|
||||||
title="Cancel">
|
|
||||||
<span class="fa fa-times"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Remove operation. -->
|
|
||||||
<button type="button" class="btn btn-default"
|
|
||||||
ng-if="operation.id"
|
|
||||||
ng-click="delete(operation, $index)"
|
|
||||||
title="remove">
|
|
||||||
<span class="fa fa-trash"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
Loading…
Reference in New Issue
Block a user