Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into dev
|
@ -46,7 +46,7 @@ module.exports = Self => {
|
|||
let filter = {where: {name: user}};
|
||||
let instance = await Self.findOne(filter);
|
||||
|
||||
if (!instance || instance.password !== md5(password))
|
||||
if (!instance || instance.password !== md5(password || ''))
|
||||
throw err;
|
||||
|
||||
let where = {id: instance.id};
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethod('validateToken', {
|
||||
description: 'Get the user information and permissions',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'token',
|
||||
type: 'String',
|
||||
description: 'The token to validate',
|
||||
required: true
|
||||
}
|
||||
],
|
||||
description: 'Validates the current logged user token',
|
||||
returns: {
|
||||
type: 'Boolean',
|
||||
root: true
|
||||
|
@ -19,13 +11,7 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.validateToken = function(tokenId, cb) {
|
||||
Self.app.models.AccessToken.findById(tokenId, (err, token) => {
|
||||
if (err) return cb(err);
|
||||
if (token)
|
||||
token.validate((_, isValid) => cb(null, isValid === true));
|
||||
else
|
||||
cb(null, false);
|
||||
});
|
||||
Self.validateToken = async function() {
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
"property": "validateToken",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"principalId": "$authenticated",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -169,9 +169,9 @@ export default class Watcher extends Component {
|
|||
*/
|
||||
check() {
|
||||
if (this.form && this.form.$invalid)
|
||||
throw new UserError(this._.instant('Some fields are invalid'));
|
||||
throw new UserError('Some fields are invalid');
|
||||
if (!this.dirty)
|
||||
throw new UserError(this._.instant('No changes to save'));
|
||||
throw new UserError('No changes to save');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import './module-loader';
|
||||
import './crud';
|
||||
import './app';
|
||||
import './acl-service';
|
||||
import './storage-services';
|
||||
import './template';
|
||||
|
|
|
@ -41,7 +41,7 @@ export function factory($http, $window, $ocLazyLoad, $translatePartialLoader, $t
|
|||
|
||||
if (validations) {
|
||||
promises.push(new Promise(resolve => {
|
||||
$http.get(`/${moduleName}/validations`).then(
|
||||
$http.get(`/${moduleName}/api/modelInfo`).then(
|
||||
json => this.onValidationsReady(json, resolve),
|
||||
() => resolve()
|
||||
);
|
||||
|
|
|
@ -13,18 +13,18 @@ export default class App {
|
|||
}
|
||||
|
||||
showMessage(message) {
|
||||
if (this.snackbar)
|
||||
this.snackbar.show({message: message});
|
||||
if (this.logger)
|
||||
this.logger.showMessage(message);
|
||||
}
|
||||
|
||||
showSuccess(message) {
|
||||
if (this.snackbar)
|
||||
this.snackbar.showSuccess({message: message});
|
||||
if (this.logger)
|
||||
this.logger.showSuccess(message);
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
if (this.snackbar)
|
||||
this.snackbar.showError({message: message});
|
||||
if (this.logger)
|
||||
this.logger.showError(message);
|
||||
}
|
||||
|
||||
pushLoader() {
|
|
@ -7,99 +7,89 @@ import UserError from 'core/lib/user-error';
|
|||
* @property {Boolean} loggedIn Whether the user is currently logged
|
||||
*/
|
||||
export default class Auth {
|
||||
constructor($http, $state, $transitions, $window, vnToken, vnModules, aclService) {
|
||||
constructor($http, $q, $state, $transitions, $window, vnToken, vnModules, aclService) {
|
||||
Object.assign(this, {
|
||||
$http,
|
||||
$q,
|
||||
$state,
|
||||
$transitions,
|
||||
$window,
|
||||
vnToken,
|
||||
vnModules,
|
||||
aclService,
|
||||
token: null,
|
||||
loggedIn: false,
|
||||
dataLoaded: false
|
||||
loggedIn: false
|
||||
});
|
||||
}
|
||||
initialize() {
|
||||
this.loggedIn = this.vnToken.token != null;
|
||||
|
||||
let criteria = {
|
||||
to: state => state.name != 'login'
|
||||
};
|
||||
this.$transitions.onStart(criteria, transition => {
|
||||
if (!this.loggedIn) {
|
||||
let params = {continue: this.$window.location.hash};
|
||||
return transition.router.stateService.target('login', params);
|
||||
}
|
||||
if (!this.dataLoaded) {
|
||||
this.resetData();
|
||||
this.dataLoaded = true;
|
||||
return this.aclService.load();
|
||||
}
|
||||
|
||||
if (this.loggedIn)
|
||||
return true;
|
||||
|
||||
let redirectToLogin = () => {
|
||||
return transition.router.stateService.target('login', {
|
||||
continue: this.$window.location.hash
|
||||
});
|
||||
};
|
||||
|
||||
if (this.vnToken.token) {
|
||||
return this.loadAcls()
|
||||
.then(() => true)
|
||||
.catch(redirectToLogin);
|
||||
} else
|
||||
return redirectToLogin();
|
||||
});
|
||||
}
|
||||
login(user, password, remember) {
|
||||
if (!user)
|
||||
throw new UserError('Please insert your user and password');
|
||||
return this.$q.reject(new UserError('Please enter your username'));
|
||||
|
||||
let params = {
|
||||
user,
|
||||
password: password || undefined
|
||||
};
|
||||
|
||||
return this.$http.post('/api/Accounts/login', params).then(
|
||||
json => this.onLoginOk(json, remember),
|
||||
json => this.onLoginErr(json)
|
||||
);
|
||||
json => this.onLoginOk(json, remember));
|
||||
}
|
||||
onLoginOk(json, remember) {
|
||||
this.resetData();
|
||||
this.vnToken.set(json.data.token, remember);
|
||||
this.loggedIn = true;
|
||||
|
||||
return this.loadAcls().then(() => {
|
||||
let continueHash = this.$state.params.continue;
|
||||
|
||||
if (continueHash)
|
||||
this.$window.location = continueHash;
|
||||
else
|
||||
this.$state.go('home');
|
||||
}
|
||||
onLoginErr(json) {
|
||||
let message;
|
||||
|
||||
switch (json.status) {
|
||||
case 401:
|
||||
message = 'Invalid credentials';
|
||||
break;
|
||||
case -1:
|
||||
message = 'Can\'t contact with server';
|
||||
break;
|
||||
default:
|
||||
message = 'Something went wrong';
|
||||
}
|
||||
|
||||
throw new UserError(message);
|
||||
});
|
||||
}
|
||||
logout() {
|
||||
let promise = this.$http.post('/api/Accounts/logout', null, {
|
||||
headers: {Authorization: this.vnToken.token}
|
||||
});
|
||||
}).catch(() => {});
|
||||
|
||||
this.vnToken.unset();
|
||||
this.loggedIn = false;
|
||||
this.resetData();
|
||||
this.vnModules.reset();
|
||||
this.aclService.reset();
|
||||
this.$state.go('login');
|
||||
|
||||
return promise;
|
||||
}
|
||||
resetData() {
|
||||
this.aclService.reset();
|
||||
loadAcls() {
|
||||
return this.aclService.load()
|
||||
.then(() => {
|
||||
this.loggedIn = true;
|
||||
this.vnModules.reset();
|
||||
this.dataLoaded = false;
|
||||
})
|
||||
.catch(err => {
|
||||
this.vnToken.unset();
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
}
|
||||
Auth.$inject = ['$http', '$state', '$transitions', '$window', 'vnToken', 'vnModules', 'aclService'];
|
||||
Auth.$inject = ['$http', '$q', '$state', '$transitions', '$window', 'vnToken', 'vnModules', 'aclService'];
|
||||
|
||||
ngModule.service('vnAuth', Auth);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
|
||||
import './app';
|
||||
import './auth';
|
||||
import './token';
|
||||
import './modules';
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
Invalid login: Invalid login, remember that distinction is made between uppercase and lowercase
|
||||
Could not contact the server: Could not contact the server, make sure you have a connection to the network
|
||||
Please enter your username: Please enter your username
|
||||
It seems that the server has fall down: It seems that the server has fall down, wait a few minutes and try again
|
||||
Session has expired: Your session has expired, please login again
|
||||
Access denied: Access denied
|
|
@ -0,0 +1,6 @@
|
|||
Invalid login: Usuario o contraseña incorrectos, recuerda que se hace distinción entre mayúsculas y minúsculas
|
||||
Could not contact the server: No se ha podido contactar con el servidor, asegurate de que dispones de conexión con la red
|
||||
Please enter your username: Por favor introduce tu nombre de usuario
|
||||
It seems that the server has fall down: Parece que el servidor se ha caído, espera unos minutos e inténtalo de nuevo
|
||||
Session has expired: Tu sesión ha expirado, por favor vuelve a iniciar sesión
|
||||
Access denied: Acción no permitida
|
|
@ -6,17 +6,12 @@ import ngModule from '../module';
|
|||
* @property {String} token The current login token or %null
|
||||
*/
|
||||
export default class Token {
|
||||
constructor($cookies) {
|
||||
this.$cookies = $cookies;
|
||||
|
||||
constructor() {
|
||||
try {
|
||||
this.token = sessionStorage.getItem('vnToken');
|
||||
if (!this.token)
|
||||
this.token = localStorage.getItem('vnToken');
|
||||
} catch (e) {}
|
||||
|
||||
if (!this.token)
|
||||
this.token = this.$cookies.get('vnToken');
|
||||
}
|
||||
set(value, remember) {
|
||||
this.unset();
|
||||
|
@ -25,27 +20,15 @@ export default class Token {
|
|||
localStorage.setItem('vnToken', value);
|
||||
else
|
||||
sessionStorage.setItem('vnToken', value);
|
||||
} catch (e) {
|
||||
let options = {};
|
||||
} catch (e) {}
|
||||
|
||||
if (location.protocol == 'https:')
|
||||
options.secure = true;
|
||||
if (remember) {
|
||||
let now = new Date().getTime();
|
||||
options.expires = new Date(now + 7 * 86400000);
|
||||
}
|
||||
|
||||
this.$cookies.put('vnToken', value, options);
|
||||
}
|
||||
this.token = value;
|
||||
}
|
||||
unset() {
|
||||
localStorage.removeItem('vnToken');
|
||||
sessionStorage.removeItem('vnToken');
|
||||
this.$cookies.remove('vnToken');
|
||||
this.token = null;
|
||||
}
|
||||
}
|
||||
Token.$inject = ['$cookies'];
|
||||
|
||||
ngModule.service('vnToken', Token);
|
||||
|
|
|
@ -3,7 +3,6 @@ import '@babel/polyfill';
|
|||
import * as ng from 'angular';
|
||||
export {ng};
|
||||
|
||||
import 'angular-cookies';
|
||||
import 'angular-translate';
|
||||
import 'angular-translate-loader-partial';
|
||||
import '@uirouter/angularjs';
|
||||
|
@ -11,7 +10,6 @@ import 'mg-crud';
|
|||
import 'oclazyload';
|
||||
|
||||
export const ngDeps = [
|
||||
'ngCookies',
|
||||
'pascalprecht.translate',
|
||||
'ui.router',
|
||||
'mgCrud',
|
||||
|
|
|
@ -31,11 +31,6 @@
|
|||
"resolved": "https://registry.npmjs.org/angular/-/angular-1.7.5.tgz",
|
||||
"integrity": "sha512-760183yxtGzni740IBTieNuWLtPNAoMqvmC0Z62UoU0I3nqk+VJuO3JbQAXOyvo3Oy/ZsdNQwrSTh/B0OQZjNw=="
|
||||
},
|
||||
"angular-cookies": {
|
||||
"version": "1.7.5",
|
||||
"resolved": "https://registry.npmjs.org/angular-cookies/-/angular-cookies-1.7.5.tgz",
|
||||
"integrity": "sha512-/8xvvSl/Z9Vwu8ChRm+OQE3vmli8Icwl8uTYkHqD7j7cknJP9kNaf7SgsENlsLVtOqLE/I7TCFYrSx3bmSeNQA=="
|
||||
},
|
||||
"angular-translate": {
|
||||
"version": "2.18.1",
|
||||
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.1.tgz",
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
"@babel/polyfill": "^7.2.5",
|
||||
"@uirouter/angularjs": "^1.0.20",
|
||||
"angular": "^1.7.5",
|
||||
"angular-cookies": "^1.7.5",
|
||||
"angular-translate": "^2.18.1",
|
||||
"angular-translate-loader-partial": "^2.18.1",
|
||||
"flatpickr": "^4.5.2",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<vn-topbar ng-if="$ctrl.inApp">
|
||||
<vn-topbar ng-if="$ctrl.showTopbar">
|
||||
<a class="logo" ui-sref="home" title="{{'Home' | translate}}">
|
||||
<img src="./logo.svg" alt="Logo"></img>
|
||||
</a>
|
||||
|
@ -12,7 +12,7 @@
|
|||
</vn-topbar>
|
||||
<div ui-view
|
||||
class="main-view"
|
||||
ng-class="{'padding': $ctrl.inApp}">
|
||||
ng-class="{'padding': $ctrl.showTopbar}">
|
||||
<vn-home></vn-home>
|
||||
</div>
|
||||
<div
|
||||
|
|
|
@ -2,42 +2,63 @@ import ngModule from '../../module';
|
|||
import './style.scss';
|
||||
|
||||
export default class App {
|
||||
constructor($element, $scope, vnApp, $state, $transitions) {
|
||||
this.$element = $element;
|
||||
this.$ = $scope;
|
||||
this.vnApp = vnApp;
|
||||
this.$state = $state;
|
||||
constructor($, $element, vnApp, $state, $transitions) {
|
||||
Object.assign(this, {
|
||||
$,
|
||||
$element,
|
||||
vnApp,
|
||||
$state
|
||||
});
|
||||
|
||||
$transitions.onStart({}, () => {
|
||||
if (this.menuShown) this.hideMenu();
|
||||
});
|
||||
}
|
||||
|
||||
$postLink() {
|
||||
this.background = this.$element[0].querySelector('.background');
|
||||
this.vnApp.snackbar = this.$.snackbar;
|
||||
this.vnApp.logger = this;
|
||||
}
|
||||
// TODO: Temporary fix to hide the topbar when login is displayed
|
||||
get inApp() {
|
||||
|
||||
$onDestroy() {
|
||||
this.vnApp.logger = null;
|
||||
}
|
||||
|
||||
get showTopbar() {
|
||||
let state = this.$state.current.name;
|
||||
return state && state != 'login';
|
||||
}
|
||||
|
||||
get leftBlock() {
|
||||
return this.$element[0].querySelector('.left-block');
|
||||
}
|
||||
|
||||
showMenu() {
|
||||
let leftBlock = this.leftBlock;
|
||||
if (!leftBlock) return;
|
||||
leftBlock.classList.add('shown');
|
||||
this.menuShown = true;
|
||||
}
|
||||
|
||||
hideMenu() {
|
||||
this.menuShown = false;
|
||||
let leftBlock = this.leftBlock;
|
||||
if (!leftBlock) return;
|
||||
leftBlock.classList.remove('shown');
|
||||
}
|
||||
|
||||
showMessage(message) {
|
||||
this.$.snackbar.show({message: message});
|
||||
}
|
||||
App.$inject = ['$element', '$scope', 'vnApp', '$state', '$transitions'];
|
||||
|
||||
showSuccess(message) {
|
||||
this.$.snackbar.showSuccess({message: message});
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
this.$.snackbar.showError({message: message});
|
||||
}
|
||||
}
|
||||
App.$inject = ['$scope', '$element', 'vnApp', '$state', '$transitions'];
|
||||
|
||||
ngModule.component('vnApp', {
|
||||
template: require('./app.html'),
|
||||
|
|
|
@ -5,11 +5,14 @@ import './style.scss';
|
|||
* A simple login form.
|
||||
*/
|
||||
export default class Controller {
|
||||
constructor($element, $scope, vnAuth) {
|
||||
this.$element = $element;
|
||||
this.$ = $scope;
|
||||
this.vnAuth = vnAuth;
|
||||
this.user = localStorage.getItem('lastUser');
|
||||
constructor($, $element, vnAuth) {
|
||||
Object.assign(this, {
|
||||
$,
|
||||
$element,
|
||||
vnAuth,
|
||||
user: localStorage.getItem('lastUser'),
|
||||
remember: true
|
||||
});
|
||||
}
|
||||
submit() {
|
||||
this.loading = true;
|
||||
|
@ -18,11 +21,11 @@ export default class Controller {
|
|||
localStorage.setItem('lastUser', this.user);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(error => {
|
||||
.catch(err => {
|
||||
this.loading = false;
|
||||
this.password = '';
|
||||
this.focusUser();
|
||||
throw error;
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
focusUser() {
|
||||
|
@ -30,7 +33,7 @@ export default class Controller {
|
|||
this.$.userField.focus();
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$element', '$scope', 'vnAuth'];
|
||||
Controller.$inject = ['$scope', '$element', 'vnAuth'];
|
||||
|
||||
ngModule.component('vnLogin', {
|
||||
template: require('./login.html'),
|
||||
|
|
|
@ -41,7 +41,9 @@
|
|||
data="banksData"
|
||||
select-fields="['id','bank']"
|
||||
show-field="bank"
|
||||
order="id"
|
||||
value-field="id">
|
||||
<tpl-item>{{id}}: {{bank}}</tpl-item>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
|
|
|
@ -43,6 +43,9 @@ class Controller {
|
|||
|
||||
set warehouseFk(value) {
|
||||
this.warehouse = value;
|
||||
if (value &&
|
||||
(!window.localStorage.localWarehouseFk || window.localStorage.localWarehouseFk === 'null'))
|
||||
window.localStorage.defaultWarehouseFk = value;
|
||||
this.setUserConfig('warehouseFk', value);
|
||||
}
|
||||
|
||||
|
@ -52,6 +55,9 @@ class Controller {
|
|||
|
||||
set companyFk(value) {
|
||||
this.company = value;
|
||||
if (value &&
|
||||
(!window.localStorage.localCompanyFk || window.localStorage.localCompanyFk === 'null'))
|
||||
window.localStorage.defaultCompanyFk = value;
|
||||
this.setUserConfig('companyFk', value);
|
||||
}
|
||||
|
||||
|
@ -76,14 +82,14 @@ class Controller {
|
|||
.then(res => {
|
||||
if (res.data && res.data.warehouseFk) {
|
||||
this.warehouse = res.data.warehouseFk;
|
||||
if (!localStorage.getItem('localWarehouseFk'))
|
||||
localStorage.setItem('defaultWarehouseFk', res.data.warehouseFk);
|
||||
if (res.data.warehouseFk && !window.localStorage.localWarehouseFk)
|
||||
window.localStorage.defaultWarehouseFk = res.data.warehouseFk;
|
||||
}
|
||||
|
||||
if (res.data && res.data.companyFk) {
|
||||
this.company = res.data.companyFk;
|
||||
if (!localStorage.getItem('localCompanyFk'))
|
||||
localStorage.setItem('defaultCompanyFk', res.data.companyFk);
|
||||
if (res.data.companyFk && !window.localStorage.localCompanyFk)
|
||||
window.localStorage.defaultCompanyFk = res.data.companyFk;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -99,8 +105,10 @@ class Controller {
|
|||
}
|
||||
|
||||
$onInit() {
|
||||
if (localStorage.getItem('localBankFk'))
|
||||
window.localStorage.defaultBankFk = localStorage.getItem('localBankFk');
|
||||
if (window.localStorage.localBankFk && window.localStorage.localBankFk !== 'null')
|
||||
window.localStorage.defaultBankFk = window.localStorage.localBankFk;
|
||||
else
|
||||
localStorage.removeItem('defaultBankFk');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html ng-app="salix">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=no"/>
|
||||
<meta name="mobile-web-app-capable" content="yes"/>
|
||||
<title vn-title translate></title>
|
||||
<script src="/acl"></script>
|
||||
</head>
|
||||
|
|
|
@ -5,7 +5,6 @@ Logout: Logout
|
|||
Change language: Change language
|
||||
Profile: Profile
|
||||
Data saved!: Data saved!
|
||||
Can't contact with server: Can't contact with server
|
||||
Push on applications menu: To open a module push on applications menu
|
||||
Clients: Clients
|
||||
Modules access: Modules access
|
|
@ -1,5 +1,4 @@
|
|||
Applications: Aplicaciones
|
||||
Can't contact with server: No se pudo contactar con el servidor
|
||||
Change language: Cambiar idioma
|
||||
Client Frozen: Cliente congelado
|
||||
Client has debt: Cliente con riesgo
|
||||
|
@ -25,6 +24,6 @@ Return to module index: Volver a la página principal del módulo
|
|||
Routes: Rutas
|
||||
What is new: Novedades de la versión
|
||||
Web Account inactive: Sin acceso Web
|
||||
Orders: Pedidos
|
||||
Orders: Cesta
|
||||
Agencies: Agencias
|
||||
Travels: Envíos
|
||||
|
|
|
@ -66,10 +66,12 @@ ngModule.config(config);
|
|||
|
||||
// Unhandled exceptions
|
||||
|
||||
$exceptionHandler.$inject = ['vnApp', '$window', '$state'];
|
||||
function $exceptionHandler(vnApp, $window, $state) {
|
||||
$exceptionHandler.$inject = ['vnApp', '$window', '$state', '$injector'];
|
||||
function $exceptionHandler(vnApp, $window, $state, $injector) {
|
||||
return function(exception, cause) {
|
||||
let message;
|
||||
let messageT;
|
||||
let $translate = $injector.get('$translate');
|
||||
|
||||
if (exception.name == 'HttpError') {
|
||||
switch (exception.xhrStatus) {
|
||||
|
@ -78,27 +80,44 @@ function $exceptionHandler(vnApp, $window, $state) {
|
|||
return;
|
||||
}
|
||||
|
||||
switch (exception.status) {
|
||||
case 401:
|
||||
if ($state.current.name != 'login') {
|
||||
messageT = 'Session has expired';
|
||||
let params = {continue: $window.location.hash};
|
||||
$state.go('login', params);
|
||||
} else
|
||||
messageT = 'Invalid login';
|
||||
break;
|
||||
case 403:
|
||||
messageT = 'Access denied';
|
||||
break;
|
||||
case 502:
|
||||
messageT = 'It seems that the server has fall down';
|
||||
break;
|
||||
case -1:
|
||||
messageT = 'Could not contact the server';
|
||||
break;
|
||||
}
|
||||
|
||||
if (!messageT) {
|
||||
let data = exception.data;
|
||||
|
||||
if (data && data.error instanceof Object)
|
||||
message = data.error.message;
|
||||
else if (exception.status === -1)
|
||||
message = `Can't contact with server`;
|
||||
else
|
||||
message = `${exception.status}: ${exception.statusText}`;
|
||||
|
||||
if (exception.status === 401 && $state.current.name != 'login') {
|
||||
let params = {continue: $window.location.hash};
|
||||
$state.go('login', params);
|
||||
}
|
||||
} else if (exception.name == 'UserError')
|
||||
message = exception.message;
|
||||
messageT = exception.message;
|
||||
else {
|
||||
vnApp.showError('Ups! Something went wrong');
|
||||
console.error(exception);
|
||||
throw exception;
|
||||
}
|
||||
|
||||
if (messageT)
|
||||
message = $translate.instant(messageT);
|
||||
vnApp.showError(message);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -91,9 +91,12 @@ dockerAndBackTest.description = `Restarts database and runs the backend tests`;
|
|||
|
||||
function backTest(done) {
|
||||
const nodemon = require('gulp-nodemon');
|
||||
let gulpBin = isWindows
|
||||
? 'node_modules/.bin/gulp.cmd'
|
||||
: 'node_modules/.bin/gulp';
|
||||
|
||||
nodemon({
|
||||
script: 'node_modules/.bin/gulp',
|
||||
exec: gulpBin,
|
||||
args: ['dockerAndBackTest'],
|
||||
watch: ['loopback', 'modules/*/back/**', 'back'],
|
||||
done: done
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
<html ng-app="salix">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=no"/>
|
||||
<meta name="mobile-web-app-capable" content="yes"/>
|
||||
<title vn-title translate></title>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -1,4 +1,63 @@
|
|||
module.exports = function(app) {
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('modelInfo', {
|
||||
description: 'Gets all models information',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'ctx',
|
||||
type: 'Object',
|
||||
http: {source: 'context'}
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'Object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/modelInfo`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.modelInfo = async function(ctx) {
|
||||
let json = {};
|
||||
let models = Self.app.models;
|
||||
|
||||
for (let modelName in models) {
|
||||
let model = models[modelName];
|
||||
let validations = model.validations;
|
||||
let jsonValidations = {};
|
||||
|
||||
for (let fieldName in validations) {
|
||||
let jsonField = [];
|
||||
|
||||
for (let validation of validations[fieldName]) {
|
||||
let options = validation.options;
|
||||
|
||||
if ((options && options.async) ||
|
||||
(validation.validation == 'custom' && !validation.isExportable))
|
||||
continue;
|
||||
|
||||
let validationCp = Object.assign({}, validation);
|
||||
|
||||
if (validationCp.message)
|
||||
validationCp.message = ctx.req.__(validationCp.message);
|
||||
|
||||
jsonField.push(toJson(validationCp));
|
||||
}
|
||||
|
||||
jsonValidations[fieldName] = jsonField;
|
||||
}
|
||||
|
||||
json[modelName] = {
|
||||
properties: model.definition.rawProperties,
|
||||
validations: jsonValidations
|
||||
};
|
||||
}
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
function toJson(object) {
|
||||
let json = {};
|
||||
|
||||
|
@ -20,44 +79,4 @@ module.exports = function(app) {
|
|||
|
||||
return json;
|
||||
}
|
||||
|
||||
app.get('/validations', function(req, res) {
|
||||
let json = {};
|
||||
let models = app.models;
|
||||
|
||||
for (let modelName in models) {
|
||||
let model = models[modelName];
|
||||
let validations = model.validations;
|
||||
let jsonValidations = {};
|
||||
|
||||
for (let fieldName in validations) {
|
||||
let jsonField = [];
|
||||
|
||||
for (let validation of validations[fieldName]) {
|
||||
let options = validation.options;
|
||||
|
||||
if ((options && options.async) ||
|
||||
(validation.validation == 'custom' && !validation.isExportable))
|
||||
continue;
|
||||
|
||||
let validationCp = Object.assign({}, validation);
|
||||
|
||||
if (validationCp.message)
|
||||
validationCp.message = req.__(validationCp.message);
|
||||
|
||||
jsonField.push(toJson(validationCp));
|
||||
}
|
||||
|
||||
jsonValidations[fieldName] = jsonField;
|
||||
}
|
||||
|
||||
json[modelName] = {
|
||||
properties: model.definition.rawProperties,
|
||||
validations: jsonValidations
|
||||
};
|
||||
}
|
||||
|
||||
res.set('Content-Type', 'application/json');
|
||||
res.send(JSON.stringify(json));
|
||||
});
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
module.exports = function(Self) {
|
||||
require('../methods/schema/model-info')(Self);
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "Schema",
|
||||
"base": "PersistedModel",
|
||||
"acls": [
|
||||
{
|
||||
"property": "validations",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"default": "/login",
|
||||
"salix": "/login"
|
||||
}
|
|
@ -7,4 +7,12 @@ module.exports = function(app) {
|
|||
version: bootTimestamp
|
||||
});
|
||||
});
|
||||
|
||||
// FIXME: Fix until the original can be used
|
||||
app.get('/api/modelInfo', function(req, res) {
|
||||
app.models.Schema.modelInfo({req}).then(json => {
|
||||
res.set('Content-Type', 'application/json');
|
||||
res.send(JSON.stringify(json));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -43,5 +43,8 @@
|
|||
},
|
||||
"user": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Schema": {
|
||||
"dataSource": "vn"
|
||||
}
|
||||
}
|
|
@ -36,11 +36,6 @@ let buildDir = `${appDir}/dist`;
|
|||
let servicesDir = `${appDir}/modules`;
|
||||
let modulesPath = `${appDir}/modules.yml`;
|
||||
|
||||
// TODO: It should be stored at some config file
|
||||
app.set('api key', 'salix');
|
||||
app.set('url auth', '/auth');
|
||||
app.set('applications', require('./application.json'));
|
||||
|
||||
app.use(cookieParser());
|
||||
|
||||
// Internationalization
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethod('getLanded', {
|
||||
description: 'Returns the first shipped and landed possible for params',
|
||||
accessType: 'READ',
|
||||
accepts: [{
|
||||
arg: 'params',
|
||||
type: 'object',
|
||||
required: true,
|
||||
description: 'shipped, addressFk, agencyModeFk, warehouseFk'
|
||||
}],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/getLanded`,
|
||||
verb: 'get'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getLanded = async params => {
|
||||
let query = `CALL vn.agencyHourGetLanded(?, ?, ?, ?);
|
||||
SELECT * FROM tmp.agencyHourGetLanded`;
|
||||
let result = await Self.rawSql(query, [params.shipped, params.addressFk || null, params.agencyModeFk, params.warehouseFk]);
|
||||
|
||||
return result[1][0].landed;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethod('getShipped', {
|
||||
description: 'Returns the first shipped possible for params',
|
||||
accessType: 'READ',
|
||||
accepts: [{
|
||||
arg: 'params',
|
||||
type: 'object',
|
||||
required: true,
|
||||
description: 'landed, addressFk, agencyModeFk'
|
||||
}],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/getShipped`,
|
||||
verb: 'get'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getShipped = async params => {
|
||||
let query = `CALL vn.agencyHourGetShipped(?, ?, ?);
|
||||
SELECT * FROM tmp.agencyHourGetShipped`;
|
||||
let result = await Self.rawSql(query, [params.landed, params.addressFk, params.agencyModeFk]);
|
||||
|
||||
return result[1][0].shipped;
|
||||
};
|
||||
};
|
|
@ -2,4 +2,6 @@ module.exports = Self => {
|
|||
require('../methods/agency/landsThatDay')(Self);
|
||||
require('../methods/agency/getFirstShipped')(Self);
|
||||
require('../methods/agency/getAgenciesWithWarehouse')(Self);
|
||||
require('../methods/agency/getLanded')(Self);
|
||||
require('../methods/agency/getShipped')(Self);
|
||||
};
|
||||
|
|
|
@ -24,6 +24,7 @@ class Controller {
|
|||
this.$state.go('zone.index');
|
||||
});
|
||||
}
|
||||
console.log('res', response);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,15 @@
|
|||
<a translate-attr="{title: 'Preview'}" ui-sref="claim.card.summary({id: $ctrl.claim.id})">
|
||||
<vn-icon icon="desktop_windows"></vn-icon>
|
||||
</a>
|
||||
<div></div>
|
||||
<vn-icon-menu
|
||||
vn-id="more-button"
|
||||
icon="more_vert"
|
||||
show-filter="false"
|
||||
value-field="callback"
|
||||
translate-fields="['name']"
|
||||
data="$ctrl.moreOptions"
|
||||
on-change="$ctrl.onMoreChange(value)">
|
||||
</vn-icon-menu>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="attributes">
|
||||
|
@ -58,3 +66,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<vn-confirm
|
||||
vn-id="confirm-dialog"
|
||||
on-response="$ctrl.returnDialog(response)"
|
||||
question="Pickup order"
|
||||
message="Do you want to send it directly?">
|
||||
</vn-confirm>
|
|
@ -1,8 +1,15 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
class Controller {
|
||||
constructor($state) {
|
||||
constructor($scope, $state, $http, $translate, vnApp) {
|
||||
this.$scope = $scope;
|
||||
this.$state = $state;
|
||||
this.$http = $http;
|
||||
this.$translate = $translate;
|
||||
this.vnApp = vnApp;
|
||||
this.moreOptions = [
|
||||
{callback: this.showConfirmDialog, name: 'Pickup order'}
|
||||
];
|
||||
}
|
||||
|
||||
get claim() {
|
||||
|
@ -30,9 +37,28 @@ class Controller {
|
|||
get quicklinks() {
|
||||
return this._quicklinks;
|
||||
}
|
||||
|
||||
onMoreChange(callback) {
|
||||
callback.call(this);
|
||||
}
|
||||
|
||||
Controller.$inject = ['$state'];
|
||||
showConfirmDialog() {
|
||||
this.$scope.confirmDialog.show();
|
||||
}
|
||||
|
||||
returnDialog(response) {
|
||||
if (response === 'CANCEL') {
|
||||
let url = `/report/rpt-claim-pickup-order?claimFk=${this.claim.id}`;
|
||||
window.open(url);
|
||||
} else if (response === 'ACCEPT') {
|
||||
this.$http.post(`/email/claim-pickup-order`, {claimFk: this.claim.id}).then(
|
||||
() => this.vnApp.showMessage(this.$translate.instant('Notification sent!'))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$scope', '$state', '$http', '$translate', 'vnApp'];
|
||||
|
||||
ngModule.component('vnClaimDescriptor', {
|
||||
template: require('./index.html'),
|
||||
|
|
|
@ -7,6 +7,8 @@ Remove sale: Borrar linea
|
|||
Claim Id: Id reclamación
|
||||
Created: Creado
|
||||
Enter a new search: Introduce una nueva búsqueda
|
||||
Pickup order: Orden de recogida
|
||||
Do you want to send it directly?: ¿Quieres enviarlo directamente?
|
||||
|
||||
#sections
|
||||
Claims: Reclamaciones
|
||||
|
|
|
@ -23,10 +23,9 @@ class Controller {
|
|||
if (this.$stateParams.amountPaid)
|
||||
this.receipt.amountPaid = this.$stateParams.amountPaid;
|
||||
|
||||
if (this.$stateParams.companyFk) {
|
||||
if (this.$stateParams.companyFk)
|
||||
this.receipt.companyFk = this.$stateParams.companyFk;
|
||||
}
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
let filter = {
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
url="/client/api/receipts/filter"
|
||||
params="$ctrl.params"
|
||||
limit="20"
|
||||
data="$ctrl.risks">
|
||||
data="$ctrl.risks"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<vn-crud-model
|
||||
vn-id="riskModel"
|
||||
url="/client/api/ClientRisks"
|
||||
filter="::$ctrl.filter"
|
||||
filter="$ctrl.filter"
|
||||
data="riskTotal"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
|
|
|
@ -18,6 +18,7 @@ class Controller {
|
|||
},
|
||||
where: {
|
||||
clientFk: $stateParams.id,
|
||||
companyFk: this.companyFk
|
||||
},
|
||||
};
|
||||
this.params = {
|
||||
|
@ -29,7 +30,9 @@ class Controller {
|
|||
}
|
||||
setOrder(value) {
|
||||
this.params.params.companyFk = value;
|
||||
this.filter.where.companyFk = value;
|
||||
this.$.model.refresh();
|
||||
this.$.riskModel.refresh();
|
||||
}
|
||||
|
||||
set risks(value) {
|
||||
|
|
|
@ -57,6 +57,24 @@ module.exports = Self => {
|
|||
throw new UserError(`You can't create a ticket for a client that has a debt`);
|
||||
}
|
||||
|
||||
if (!params.shipped && params.landed) {
|
||||
params.shipped = await Self.app.models.Agency.getShipped({
|
||||
landed: params.landed,
|
||||
addressFk: address.id,
|
||||
agencyModeFk: params.agencyModeFk
|
||||
});
|
||||
}
|
||||
|
||||
if (params.shipped && !params.landed) {
|
||||
params.landed = await Self.app.models.Agency.getLanded({
|
||||
shipped: params.shipped,
|
||||
addressFk: address.id,
|
||||
agencyModeFk: params.agencyModeFk,
|
||||
warehouseFk: params.warehouseFk
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (!params.userId && ctx.req && ctx.req.accessToken.userId)
|
||||
params.userId = ctx.req.accessToken.userId;
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
ini-options="{enableTime: false}">
|
||||
</vn-date-picker>
|
||||
<vn-autocomplete
|
||||
disabled="!$ctrl.clientFk || !$ctrl.landed"
|
||||
disabled="!$ctrl.warehouseFk && (!$ctrl.clientFk || !$ctrl.landed)"
|
||||
field="$ctrl.warehouseFk"
|
||||
url="/agency/api/Warehouses"
|
||||
show-field="name"
|
||||
|
|
|
@ -14,6 +14,9 @@ class Controller {
|
|||
$onInit() {
|
||||
if (this.$stateParams && this.$stateParams.clientFk)
|
||||
this.clientFk = this.$stateParams.clientFk;
|
||||
|
||||
if (window.localStorage && window.localStorage.defaultWarehouseFk)
|
||||
this.warehouseFk = parseInt(window.localStorage.defaultWarehouseFk);
|
||||
}
|
||||
|
||||
set ticket(value) {
|
||||
|
@ -37,6 +40,7 @@ class Controller {
|
|||
});
|
||||
} else
|
||||
this.addressFk = null;
|
||||
this.getAvailableAgencies();
|
||||
}
|
||||
|
||||
get clientFk() {
|
||||
|
@ -45,6 +49,7 @@ class Controller {
|
|||
|
||||
set addressFk(value) {
|
||||
this.ticket.addressFk = value;
|
||||
this.getAvailableAgencies();
|
||||
}
|
||||
|
||||
get addressFk() {
|
||||
|
@ -53,6 +58,7 @@ class Controller {
|
|||
|
||||
set landed(value) {
|
||||
this.ticket.landed = value;
|
||||
this.getAvailableAgencies();
|
||||
}
|
||||
|
||||
get landed() {
|
||||
|
@ -67,10 +73,9 @@ class Controller {
|
|||
return this.ticket.warehouseFk;
|
||||
}
|
||||
|
||||
|
||||
getAvailableAgencies() {
|
||||
if (this.ticket.warehouseFk && this.ticket.addressFk && this.ticket.landed && this.ticket.clientFk) {
|
||||
this.ticket.agencyModeFk = null;
|
||||
if (this.ticket.landed && this.ticket.addressFk) {
|
||||
let filter = {warehouseFk: this.ticket.warehouseFk, addressFk: this.ticket.addressFk, landed: this.ticket.landed};
|
||||
filter = encodeURIComponent(JSON.stringify(filter));
|
||||
let query = `/api/Agencies/getAgenciesWithWarehouse?filter=${filter}`;
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
.container {
|
||||
font-family: verdana, sans-serif;
|
||||
font-size: 16px;
|
||||
zoom: 0.55
|
||||
}
|
||||
|
||||
.columns {
|
||||
overflow: hidden
|
||||
}
|
||||
|
@ -27,46 +33,6 @@
|
|||
float: left
|
||||
}
|
||||
|
||||
.pull-left {
|
||||
float: left
|
||||
}
|
||||
|
||||
.pull-right {
|
||||
float: right
|
||||
}
|
||||
|
||||
.grid {
|
||||
border-bottom: 3px solid #888888
|
||||
}
|
||||
|
||||
.grid .row {
|
||||
padding: 5px;
|
||||
margin-bottom: 0
|
||||
}
|
||||
|
||||
.grid .header {
|
||||
border-bottom: 1px solid #808080;
|
||||
border-top: 1px solid #808080;
|
||||
background-color: #c0c0c0;
|
||||
}
|
||||
|
||||
|
||||
.grid .row.inline > div {
|
||||
float: left;
|
||||
}
|
||||
|
||||
|
||||
.panel {
|
||||
border: 1px solid #DDD;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
padding:10px
|
||||
}
|
||||
|
||||
.panel .header {
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-bottom: 15px;
|
||||
overflow: hidden
|
||||
|
@ -139,6 +105,37 @@
|
|||
overflow: visible
|
||||
}
|
||||
|
||||
.grid {
|
||||
border-bottom: 3px solid #888888
|
||||
}
|
||||
|
||||
.grid .row {
|
||||
padding: 5px;
|
||||
margin-bottom: 0
|
||||
}
|
||||
|
||||
.grid .header {
|
||||
border-bottom: 1px solid #808080;
|
||||
border-top: 1px solid #808080;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
|
||||
.grid .row.inline > div {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.panel {
|
||||
border: 1px solid #DDD;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
padding:10px
|
||||
}
|
||||
|
||||
.panel .header {
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
.box {
|
||||
border-top: 1px solid #CCC;
|
||||
border-right: 1px solid #CCC;
|
||||
|
@ -164,22 +161,3 @@
|
|||
.pull-right {
|
||||
float: right
|
||||
}
|
||||
|
||||
.grid {
|
||||
border-bottom: 3px solid #888888
|
||||
}
|
||||
|
||||
.grid .row {
|
||||
padding: 5px;
|
||||
margin-bottom: 0
|
||||
}
|
||||
|
||||
.grid .header {
|
||||
border-bottom: 1px solid #808080;
|
||||
border-top: 1px solid #808080;
|
||||
background-color: #c0c0c0;
|
||||
}
|
||||
|
||||
.grid .row.inline > div {
|
||||
float: left;
|
||||
}
|
|
@ -4,9 +4,10 @@
|
|||
{"type": "email", "name": "payment-update"},
|
||||
{"type": "email", "name": "letter-debtor-st"},
|
||||
{"type": "email", "name": "letter-debtor-nd"},
|
||||
{"type": "email", "name": "claim-pickup-order"},
|
||||
{"type": "report", "name": "delivery-note"},
|
||||
{"type": "report", "name": "invoice"},
|
||||
{"type": "report", "name": "claim-pickup"},
|
||||
{"type": "report", "name": "rpt-claim-pickup-order"},
|
||||
{"type": "static", "name": "email-header"},
|
||||
{"type": "static", "name": "email-footer"},
|
||||
{"type": "static", "name": "report-header"},
|
||||
|
|
|
@ -4,18 +4,16 @@ const renderer = require('vue-server-renderer').createRenderer();
|
|||
const fs = require('fs-extra');
|
||||
const juice = require('juice');
|
||||
const smtp = require('./smtp');
|
||||
const i18n = new VueI18n({
|
||||
locale: 'es',
|
||||
});
|
||||
|
||||
Vue.use(VueI18n);
|
||||
const fallbackLocale = 'es';
|
||||
|
||||
if (!process.env.OPENSSL_CONF)
|
||||
process.env.OPENSSL_CONF = '/etc/ssl/';
|
||||
|
||||
Vue.use(VueI18n);
|
||||
|
||||
module.exports = {
|
||||
|
||||
path: `${appPath}/reports`,
|
||||
path: `${appPath}/report`,
|
||||
|
||||
/**
|
||||
* Renders a report component
|
||||
|
@ -25,17 +23,18 @@ module.exports = {
|
|||
*/
|
||||
async render(name, ctx) {
|
||||
const component = require(`${this.path}/${name}`);
|
||||
const prefetchedData = await this.preFetch(component, ctx);
|
||||
|
||||
const app = new Vue({
|
||||
i18n,
|
||||
render: h => h(component),
|
||||
const result = await this.preFetch(component, ctx);
|
||||
const i18n = new VueI18n({
|
||||
locale: 'es',
|
||||
fallbackLocale
|
||||
});
|
||||
const app = new Vue({i18n,
|
||||
render: h => h(result.component)});
|
||||
|
||||
return renderer.renderToString(app).then(renderedHtml => {
|
||||
return {
|
||||
html: renderedHtml,
|
||||
data: prefetchedData,
|
||||
data: result.mergedData,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
@ -43,10 +42,11 @@ module.exports = {
|
|||
/**
|
||||
* Prefetch all component data from asyncData method
|
||||
*
|
||||
* @param {Object} component - Component object
|
||||
* @param {Object} orgComponent - Component object
|
||||
* @param {Object} ctx - Request context
|
||||
*/
|
||||
async preFetch(component, ctx) {
|
||||
async preFetch(orgComponent, ctx) {
|
||||
let component = Object.create(orgComponent);
|
||||
let mergedData = {attachments: []};
|
||||
let asyncData = {};
|
||||
let data = {};
|
||||
|
@ -60,14 +60,18 @@ module.exports = {
|
|||
|
||||
await this.attachAssets(component);
|
||||
|
||||
if (component.hasOwnProperty('data'))
|
||||
data = component.data();
|
||||
if (orgComponent.hasOwnProperty('data'))
|
||||
data = orgComponent.data();
|
||||
|
||||
if (component.hasOwnProperty('asyncData')) {
|
||||
asyncData = await component.asyncData(ctx, params);
|
||||
if (orgComponent.hasOwnProperty('asyncData')) {
|
||||
asyncData = await orgComponent.asyncData(ctx, params);
|
||||
|
||||
if (asyncData.locale) {
|
||||
const locale = component.i18n.messages[asyncData.locale];
|
||||
let locale = component.i18n.messages[asyncData.locale];
|
||||
|
||||
if (!locale)
|
||||
locale = component.i18n.messages[fallbackLocale];
|
||||
|
||||
mergedData.subject = locale.subject;
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +85,7 @@ module.exports = {
|
|||
if (data.hasOwnProperty('files')) {
|
||||
const files = data.files;
|
||||
files.forEach(file => {
|
||||
const componentPath = `${this.path}/${component.name}`;
|
||||
const componentPath = `${this.path}/${orgComponent.name}`;
|
||||
let fileSrc = componentPath + file;
|
||||
|
||||
if (file.slice(0, 4) === 'http' || file.slice(0, 4) === 'https')
|
||||
|
@ -96,25 +100,30 @@ module.exports = {
|
|||
});
|
||||
}
|
||||
|
||||
if (component.components) {
|
||||
const components = component.components;
|
||||
const promises = [];
|
||||
const components = orgComponent.components;
|
||||
|
||||
Object.keys(components).forEach(component => {
|
||||
promises.push(this.preFetch(components[component], ctx));
|
||||
if (components) {
|
||||
const promises = [];
|
||||
const childNames = [];
|
||||
component.components = {};
|
||||
|
||||
Object.keys(components).forEach(childName => {
|
||||
childNames.push(childName);
|
||||
promises.push(this.preFetch(components[childName], ctx));
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(results => {
|
||||
results.forEach(result => {
|
||||
result.attachments.forEach(atth => {
|
||||
await Promise.all(promises).then(results => {
|
||||
results.forEach((result, i) => {
|
||||
result.mergedData.attachments.forEach(atth => {
|
||||
mergedData.attachments.push(atth);
|
||||
});
|
||||
|
||||
component.components[childNames[i]] = result.component;
|
||||
});
|
||||
return mergedData;
|
||||
});
|
||||
}
|
||||
|
||||
return mergedData;
|
||||
return {component, mergedData};
|
||||
},
|
||||
|
||||
async attachAssets(component) {
|
||||
|
|
|
@ -13,7 +13,7 @@ if (!process.env.OPENSSL_CONF)
|
|||
|
||||
module.exports = {
|
||||
|
||||
path: `${appPath}/reports`,
|
||||
path: `${appPath}/report`,
|
||||
|
||||
/**
|
||||
* Renders a report component
|
||||
|
@ -23,16 +23,13 @@ module.exports = {
|
|||
*/
|
||||
async render(name, ctx) {
|
||||
const component = require(`${this.path}/${name}`);
|
||||
|
||||
await this.preFetch(component, ctx);
|
||||
|
||||
const result = await this.preFetch(component, ctx);
|
||||
const i18n = new VueI18n({
|
||||
locale: 'es',
|
||||
fallbackLocale: 'en'
|
||||
});
|
||||
const app = new Vue({
|
||||
i18n,
|
||||
render: h => h(component),
|
||||
});
|
||||
const app = new Vue({i18n,
|
||||
render: h => h(result.component)});
|
||||
|
||||
return renderer.renderToString(app);
|
||||
},
|
||||
|
@ -40,10 +37,11 @@ module.exports = {
|
|||
/**
|
||||
* Prefetch all component data from asyncData method
|
||||
*
|
||||
* @param {Object} component - Component object
|
||||
* @param {Object} orgComponent - Component object
|
||||
* @param {Object} ctx - Request context
|
||||
*/
|
||||
async preFetch(component, ctx) {
|
||||
async preFetch(orgComponent, ctx) {
|
||||
let component = Object.create(orgComponent);
|
||||
let mergedData = {};
|
||||
let asyncData = {};
|
||||
let data = {};
|
||||
|
@ -57,11 +55,11 @@ module.exports = {
|
|||
|
||||
await this.attachAssets(component);
|
||||
|
||||
if (component.hasOwnProperty('data'))
|
||||
data = component.data();
|
||||
if (orgComponent.hasOwnProperty('data'))
|
||||
data = orgComponent.data();
|
||||
|
||||
if (component.hasOwnProperty('asyncData'))
|
||||
asyncData = await component.asyncData(ctx, params);
|
||||
if (orgComponent.hasOwnProperty('asyncData'))
|
||||
asyncData = await orgComponent.asyncData(ctx, params);
|
||||
|
||||
mergedData = Object.assign(mergedData, data, asyncData);
|
||||
|
||||
|
@ -69,16 +67,25 @@ module.exports = {
|
|||
return mergedData;
|
||||
};
|
||||
|
||||
if (component.components) {
|
||||
const components = component.components;
|
||||
const components = orgComponent.components;
|
||||
if (components) {
|
||||
const promises = [];
|
||||
const childNames = [];
|
||||
component.components = {};
|
||||
|
||||
Object.keys(components).forEach(component => {
|
||||
promises.push(this.preFetch(components[component], ctx));
|
||||
Object.keys(components).forEach(childName => {
|
||||
childNames.push(childName);
|
||||
promises.push(this.preFetch(components[childName], ctx));
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
await Promise.all(promises).then(results => {
|
||||
results.forEach((result, i) => {
|
||||
component.components[childNames[i]] = result.component;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {component};
|
||||
},
|
||||
|
||||
async attachAssets(component) {
|
||||
|
|
|
@ -4,7 +4,7 @@ const express = require('express');
|
|||
const routes = require(`../config/routes.json`);
|
||||
|
||||
module.exports = app => {
|
||||
this.path = `${appPath}/reports`;
|
||||
this.path = `${appPath}/report`;
|
||||
|
||||
/**
|
||||
* Enables a report
|
||||
|
@ -15,10 +15,9 @@ module.exports = app => {
|
|||
if (!name) throw new Error('Report name required');
|
||||
|
||||
app.get(`/report/${name}`, (request, response, next) => {
|
||||
reportEngine.toPdf(name, request).then(stream => {
|
||||
response.setHeader('Content-Disposition', `attachment; filename="${name}.pdf"`);
|
||||
response.setHeader('Content-type', 'application/pdf');
|
||||
|
||||
reportEngine.toPdf(name, request).then(stream => {
|
||||
stream.pipe(response);
|
||||
}).catch(e => {
|
||||
next(e);
|
||||
|
|
|
@ -3,10 +3,8 @@ body {
|
|||
}
|
||||
|
||||
.container {
|
||||
font-family: arial, sans-serif;
|
||||
max-width: 600px;
|
||||
min-width: 320px;
|
||||
font-size: 16px;
|
||||
margin: 0 auto;
|
||||
color: #555
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<title>{{ $t('subject') }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<section class="container">
|
||||
<email-header></email-header>
|
||||
<section class="main">
|
||||
<!-- Title block -->
|
||||
<div class="title">
|
||||
<h1>{{ $t('title') }}</h1>
|
||||
</div>
|
||||
<!-- Title block end -->
|
||||
|
||||
<p>{{$t('description.dear')}},</p>
|
||||
<p>{{$t('description.instructions')}}</p>
|
||||
|
||||
<!-- <h1>{{$t('sections.howToBuy.title')}}</h1>
|
||||
<p>{{$t('sections.howToBuy.description')}}</p>
|
||||
<ol>
|
||||
<li v-for="requeriment in $t('sections.howToBuy.requeriments')">
|
||||
<span v-html="requeriment"></span>
|
||||
</li>
|
||||
</ol>
|
||||
<p>{{$t('sections.howToBuy.stock')}}</p>
|
||||
<p>{{$t('sections.howToBuy.delivery')}}</p> -->
|
||||
</section>
|
||||
<email-footer></email-footer>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,52 @@
|
|||
const UserException = require(`${appPath}/lib/exceptions/userException`);
|
||||
const reportEngine = require(`${appPath}/lib/reportEngine`);
|
||||
const database = require(`${appPath}/lib/database`);
|
||||
const emailHeader = require('../email-header');
|
||||
const emailFooter = require('../email-footer');
|
||||
|
||||
|
||||
module.exports = {
|
||||
name: 'claim-pickup-order',
|
||||
async asyncData(ctx, params) {
|
||||
const promises = [];
|
||||
const data = {
|
||||
isPreview: ctx.method === 'GET',
|
||||
};
|
||||
|
||||
if (!params.claimFk)
|
||||
throw new UserException('No claim id specified');
|
||||
|
||||
promises.push(reportEngine.toPdf('rpt-claim-pickup-order', ctx));
|
||||
promises.push(this.methods.fetchClient(params.claimFk));
|
||||
|
||||
return Promise.all(promises).then(result => {
|
||||
const stream = result[0];
|
||||
const [[client]] = result[1];
|
||||
|
||||
Object.assign(data, client);
|
||||
Object.assign(data, {attachments: [{filename: 'claim-pickup-order.pdf', content: stream}]});
|
||||
|
||||
return data;
|
||||
});
|
||||
},
|
||||
created() {
|
||||
this.$i18n.locale = this.locale;
|
||||
},
|
||||
methods: {
|
||||
fetchClient(claimFk) {
|
||||
return database.pool.query(`
|
||||
SELECT
|
||||
c.id,
|
||||
u.lang locale,
|
||||
c.email recipient
|
||||
FROM claim cl
|
||||
JOIN client c ON c.id = cl.clientFk
|
||||
JOIN account.user u ON u.id = c.id
|
||||
WHERE cl.id = ?`, [claimFk]);
|
||||
},
|
||||
},
|
||||
components: {
|
||||
emailHeader,
|
||||
emailFooter,
|
||||
},
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
module.exports = {
|
||||
messages: {
|
||||
es: {
|
||||
subject: 'Orden de recogida',
|
||||
title: 'Orden de recogida',
|
||||
description: {
|
||||
dear: 'Estimado cliente',
|
||||
instructions: 'Aqui tienes tu orden de recogida.'
|
||||
},
|
||||
sections: {
|
||||
howToBuy: {
|
||||
title: 'Cómo hacer un pedido',
|
||||
description: `Para realizar un pedido en nuestra web,
|
||||
debes configurarlo indicando:`,
|
||||
requeriments: [
|
||||
'Si quieres recibir el pedido (por agencia o por nuestro propio reparto) o si lo prefieres recoger en alguno de nuestros almacenes.',
|
||||
'La fecha en la que quieres recibir el pedido (se preparará el día anterior).',
|
||||
'La dirección de entrega o el almacén donde quieres recoger el pedido.'],
|
||||
stock: 'En nuestra web y aplicaciones puedes visualizar el stock disponible de flor cortada, verdes, plantas, complementos y artificial. Ten en cuenta que dicho stock puede variar en función de la fecha seleccionada al configurar el pedido. Es importante CONFIRMAR los pedidos para que la mercancía quede reservada.',
|
||||
delivery: 'El reparto se realiza de lunes a sábado según la zona en la que te encuentres. Por regla general, los pedidos que se entregan por agencia, deben estar confirmados y pagados antes de las 17h del día en que se preparan (el día anterior a recibirlos), aunque esto puede variar si el pedido se envía a través de nuestro reparto y según la zona.',
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
|
@ -3,10 +3,8 @@ body {
|
|||
}
|
||||
|
||||
.container {
|
||||
font-family: arial, sans-serif;
|
||||
max-width: 600px;
|
||||
min-width: 320px;
|
||||
font-size: 16px;
|
||||
margin: 0 auto;
|
||||
color: #555
|
||||
}
|
|
@ -15,6 +15,8 @@ module.exports = {
|
|||
|
||||
return this.methods.fetchClient(params.clientFk)
|
||||
.then(([result]) => {
|
||||
if (!result)
|
||||
throw new UserException('No client data found');
|
||||
return Object.assign(data, result[0]);
|
||||
});
|
||||
},
|
|
@ -1,11 +1,3 @@
|
|||
footer {
|
||||
font-family: arial, sans-serif;
|
||||
max-width: 600px;
|
||||
min-width: 320px;
|
||||
font-size: 16px;
|
||||
margin: 0 auto
|
||||
}
|
||||
|
||||
.buttons {
|
||||
background-color: #FFF;
|
||||
text-align: center;
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
@ -0,0 +1,7 @@
|
|||
header {
|
||||
color: #555
|
||||
}
|
||||
|
||||
header img {
|
||||
width: 100%
|
||||
}
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
@ -0,0 +1,6 @@
|
|||
const CssReader = require(`${appPath}/lib/cssReader`);
|
||||
|
||||
module.exports = new CssReader([
|
||||
`${appPath}/common/css/layout.css`,
|
||||
`${__dirname}/style.css`])
|
||||
.mergeStyles();
|
|
@ -0,0 +1,39 @@
|
|||
body {
|
||||
background-color: #EEE
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 600px;
|
||||
min-width: 320px;
|
||||
margin: 0 auto;
|
||||
color: #555
|
||||
}
|
||||
.main {
|
||||
background-color: #FFF;
|
||||
padding: 20px
|
||||
}
|
||||
|
||||
.main a {
|
||||
color: #8dba25
|
||||
}
|
||||
|
||||
.main h1 {
|
||||
color: #999
|
||||
}
|
||||
|
||||
.main h3 {
|
||||
font-size: 16px
|
||||
}
|
||||
|
||||
.title {
|
||||
background-color: #95d831;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
padding: 35px 0
|
||||
}
|
||||
|
||||
.title h1 {
|
||||
font-size: 32px;
|
||||
color: #333;
|
||||
margin: 0
|
||||
}
|
|
@ -15,6 +15,8 @@ module.exports = {
|
|||
|
||||
return this.methods.fetchClientData(params.clientFk)
|
||||
.then(([result]) => {
|
||||
if (!result)
|
||||
throw new UserException('No client data found');
|
||||
return Object.assign(data, result[0]);
|
||||
});
|
||||
},
|
|
@ -0,0 +1,6 @@
|
|||
const CssReader = require(`${appPath}/lib/cssReader`);
|
||||
|
||||
module.exports = new CssReader([
|
||||
`${appPath}/common/css/layout.css`,
|
||||
`${__dirname}/style.css`])
|
||||
.mergeStyles();
|
|
@ -3,10 +3,8 @@ body {
|
|||
}
|
||||
|
||||
.container {
|
||||
font-family: arial, sans-serif;
|
||||
max-width: 600px;
|
||||
min-width: 320px;
|
||||
font-size: 16px;
|
||||
margin: 0 auto;
|
||||
color: #555
|
||||
}
|