Merge branch '1466-print_refactor' of verdnatura/salix into dev
gitea/salix/dev This commit looks good Details

LGTM
This commit is contained in:
Joan Sanchez 2019-11-13 07:41:46 +00:00 committed by Gitea
commit 729b7e9412
279 changed files with 5160 additions and 3991 deletions

View File

@ -1460,7 +1460,7 @@ INSERT INTO `vn`.`receipt`(`id`, `invoiceFk`, `amountPaid`, `amountUnpaid`, `pay
(1, 'Cobro web', 100.50, 0.00, CURDATE(), 9, 1, 101, CURDATE(), 442, 1),
(2, 'Cobro web', 200.50, 0.00, DATE_ADD(CURDATE(), INTERVAL -5 DAY), 9, 1, 101, DATE_ADD(CURDATE(), INTERVAL -5 DAY), 442, 1),
(3, 'Cobro en efectivo', 300.00, 100.00, DATE_ADD(CURDATE(), INTERVAL -10 DAY), 9, 1, 102, DATE_ADD(CURDATE(), INTERVAL -10 DAY), 442, 0),
(4, 'Cobro en efectivo', -400.00, -50.00, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 9, 1, 103, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 442, 0);
(4, 'Cobro en efectivo', 400.00, -50.00, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 9, 1, 103, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 442, 0);
INSERT INTO `vn2008`.`workerTeam`(`id`, `team`, `user`)
VALUES

View File

@ -47,6 +47,7 @@ TABLES=(
claimResult
ticketUpdateAction
state
sample
)
dump_tables ${TABLES[@]}
@ -56,7 +57,6 @@ TABLES=(
businessReasonEnd
container
department
escritos
Grupos
iva_group_codigo
tarifa_componentes

View File

@ -1,3 +1,3 @@
module.exports = function(app) {
require('../../../print/server.js')(app);
require('../../../print/boot.js')(app);
};

16
loopback/util/http.js Normal file
View File

@ -0,0 +1,16 @@
/**
* Serializes an object to a query params
*
* @param {Object} obj The params object
* @return {String} Serialized params
*/
exports.httpParamSerializer = function(obj) {
let query = '';
for (let param in obj) {
if (query != '')
query += '&';
query += `${param}=${obj[param]}`;
}
return query;
};

View File

@ -35,7 +35,7 @@ class Controller {
{
relation: 'client',
scope: {
fields: ['salesPersonFk', 'name'],
fields: ['salesPersonFk', 'name', 'email'],
include: {
relation: 'salesPerson',
scope: {

View File

@ -1,13 +1,14 @@
import ngModule from '../module';
class Controller {
constructor($scope, $state, $http, $translate, vnApp, aclService) {
constructor($scope, $state, $http, $translate, vnApp, aclService, $httpParamSerializer) {
this.$scope = $scope;
this.$state = $state;
this.$http = $http;
this.$translate = $translate;
this.vnApp = vnApp;
this.aclService = aclService;
this.$httpParamSerializer = $httpParamSerializer;
this.moreOptions = [
{callback: this.showPickupOrder, name: 'Show Pickup order'},
{callback: this.confirmPickupOrder, name: 'Send Pickup order'},
@ -60,7 +61,12 @@ class Controller {
}
showPickupOrder() {
let url = `report/rpt-claim-pickup-order?claimFk=${this.claim.id}`;
const params = {
clientId: this.claim.clientFk,
claimId: this.claim.id
};
const serializedParams = this.$httpParamSerializer(params);
let url = `api/report/claim-pickup-order?${serializedParams}`;
window.open(url);
}
@ -70,7 +76,14 @@ class Controller {
sendPickupOrder(response) {
if (response === 'accept') {
this.$http.post(`email/claim-pickup-order`, {claimFk: this.claim.id}).then(
const params = {
recipient: this.claim.client.email,
clientId: this.claim.clientFk,
claimId: this.claim.id
};
const serializedParams = this.$httpParamSerializer(params);
const url = `email/claim-pickup-order?${serializedParams}`;
this.$http.get(url).then(
() => this.vnApp.showMessage(this.$translate.instant('Notification sent!'))
);
}
@ -90,7 +103,7 @@ class Controller {
}
}
Controller.$inject = ['$scope', '$state', '$http', '$translate', 'vnApp', 'aclService'];
Controller.$inject = ['$scope', '$state', '$http', '$translate', 'vnApp', 'aclService', '$httpParamSerializer'];
ngModule.component('vnClaimDescriptor', {
template: require('./index.html'),

View File

@ -1,20 +1,27 @@
import './index.js';
describe('Item Component vnClaimDescriptor', () => {
let $httpParamSerializer;
let $httpBackend;
let controller;
beforeEach(ngModule('claim'));
beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => {
beforeEach(angular.mock.inject(($componentController, _$httpBackend_, _$httpParamSerializer_) => {
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
controller = $componentController('vnClaimDescriptor');
controller.claim = {id: 2};
controller.claim = {id: 2, clientFk: 101, client: {email: 'client@email'}};
}));
describe('showPickupOrder()', () => {
it('should open a new window showing a pickup order PDF document', () => {
let expectedPath = 'report/rpt-claim-pickup-order?claimFk=2';
const params = {
clientId: controller.claim.clientFk,
claimId: controller.claim.id
};
const serializedParams = $httpParamSerializer(params);
let expectedPath = `api/report/claim-pickup-order?${serializedParams}`;
spyOn(window, 'open');
controller.showPickupOrder();
@ -38,8 +45,15 @@ describe('Item Component vnClaimDescriptor', () => {
it('should make a query and call vnApp.showMessage() if the response is accept', () => {
spyOn(controller.vnApp, 'showMessage');
$httpBackend.when('POST', `email/claim-pickup-order`, {claimFk: 2}).respond();
$httpBackend.expect('POST', `email/claim-pickup-order`, {claimFk: 2}).respond();
const params = {
recipient: 'client@email',
clientId: controller.claim.clientFk,
claimId: controller.claim.id
};
const serializedParams = $httpParamSerializer(params);
$httpBackend.when('GET', `email/claim-pickup-order?${serializedParams}`).respond();
$httpBackend.expect('GET', `email/claim-pickup-order?${serializedParams}`).respond();
controller.sendPickupOrder('accept');
$httpBackend.flush();

View File

@ -2,6 +2,8 @@ let request = require('request-promise-native');
let UserError = require('vn-loopback/util/user-error');
let getFinalState = require('vn-loopback/util/hook').getFinalState;
let isMultiple = require('vn-loopback/util/hook').isMultiple;
const httpParamSerializer = require('vn-loopback/util/http').httpParamSerializer;
const LoopBackContext = require('loopback-context');
module.exports = Self => {
// Methods
@ -239,15 +241,18 @@ module.exports = Self => {
});
}
const options = {
method: 'POST',
uri: 'http://127.0.0.1:3000/api/email/payment-update',
body: {
clientFk: instance.id
},
json: true
// Send email to client
if (!instance.email) return;
const loopBackContext = LoopBackContext.getCurrentContext();
const headers = loopBackContext.active.http.req.headers;
const params = {
clientId: instance.id,
recipient: instance.email
};
await request(options);
const serializedParams = httpParamSerializer(params);
const query = `${headers.origin}/api/email/payment-update?${serializedParams}`;
await request.get(query);
}
});

View File

@ -1,4 +1,9 @@
<mg-ajax path="ClientSamples" options="vnPost"></mg-ajax>
<vn-crud-model auto-load="true"
url="Companies"
data="companiesData"
order="code">
</vn-crud-model>
<vn-watcher
vn-id="watcher"
data="$ctrl.clientSample"
@ -8,9 +13,13 @@
<form name="form" ng-submit="$ctrl.onSubmit()" compact>
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-autocomplete
vn-one
vn-id="sampleType"
<vn-textfield vn-one
label="Recipient"
ng-model="$ctrl.clientSample.recipient">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one vn-id="sampleType"
ng-model="$ctrl.clientSample.typeFk"
model="ClientSample.typeFk"
fields="['code','hasCompany']"
@ -19,11 +28,10 @@
value-field="id"
label="Sample">
</vn-autocomplete>
<vn-autocomplete
vn-one
ng-model="$ctrl.clientSample.companyFk"
<vn-autocomplete vn-one
ng-model="$ctrl.companyId"
model="ClientSample.companyFk"
url="Companies"
data="companiesData"
show-field="code"
value-field="id"
label="Company"
@ -41,5 +49,9 @@
<vn-dialog
vn-id="show-preview"
on-open="$ctrl.onPreviewOpen()">
<tpl-body></tpl-body>
<tpl-body class="client-sample-dialog">
<div class="loading">
<vn-spinner enable="true"></vn-spinner>
</div>
</tpl-body>
</vn-dialog>

View File

@ -1,33 +1,84 @@
import ngModule from '../../module';
import Component from 'core/lib/component';
import './style.scss';
class Controller {
constructor($scope, $state, $http, vnApp, $translate) {
this.$scope = $scope;
this.$state = $state;
this.$stateParams = $state.params;
this.$http = $http;
class Controller extends Component {
constructor($element, $, vnApp, $httpParamSerializer, vnConfig) {
super($element, $);
this.vnApp = vnApp;
this.$translate = $translate;
this.$httpParamSerializer = $httpParamSerializer;
this.vnConfig = vnConfig;
this.clientSample = {
clientFk: this.$stateParams.id
clientFk: this.$params.id,
companyFk: vnConfig.companyFk
};
}
jsonToQuery(json) {
let query = '';
for (let param in json) {
if (query != '')
query += '&';
query += `${param}=${json[param]}`;
}
get client() {
return this._client;
}
return query;
set client(value) {
this._client = value;
if (value)
this.clientSample.recipient = value.email;
}
get companyId() {
if (!this.clientSample.companyFk)
this.clientSample.companyFk = this.vnConfig.companyFk;
return this.clientSample.companyFk;
}
set companyId(value) {
this.clientSample.companyFk = value;
}
showPreview() {
let sampleType = this.$scope.sampleType.selection;
let params = {clientFk: this.$stateParams.id};
let sampleType = this.$.sampleType.selection;
if (!sampleType)
return this.vnApp.showError(this.$translate.instant('Choose a sample'));
if (sampleType.hasCompany && !this.clientSample.companyFk)
return this.vnApp.showError(this.$translate.instant('Choose a company'));
const params = {
clientId: this.$params.id,
recipient: this.clientSample.recipient,
isPreview: true
};
if (sampleType.hasCompany)
params.companyId = this.clientSample.companyFk;
const serializedParams = this.$httpParamSerializer(params);
const query = `email/${sampleType.code}?${serializedParams}`;
this.$http.get(query).then(res => {
this.$.showPreview.show();
let dialog = document.body.querySelector('div.vn-dialog');
let body = dialog.querySelector('tpl-body');
let scroll = dialog.querySelector('div:first-child');
body.innerHTML = res.data;
scroll.scrollTop = 0;
});
}
onSubmit() {
this.$.watcher.check();
this.$.watcher.realSubmit().then(() =>
this.sendSample()
);
}
sendSample() {
let sampleType = this.$.sampleType.selection;
let params = {
clientId: this.$params.id,
recipient: this.clientSample.recipient
};
if (!sampleType)
return this.vnApp.showError(this.$translate.instant('Choose a sample'));
@ -36,50 +87,22 @@ class Controller {
return this.vnApp.showError(this.$translate.instant('Choose a company'));
if (sampleType.hasCompany)
params.companyFk = this.clientSample.companyFk;
params.companyId = this.clientSample.companyFk;
let query = `email/${sampleType.code}?${this.jsonToQuery(params)}`;
const serializedParams = this.$httpParamSerializer(params);
const query = `email/${sampleType.code}?${serializedParams}`;
this.$http.get(query).then(res => {
if (res.data) {
let dialog = this.$scope.showPreview.element;
let body = dialog.querySelector('tpl-body');
let scroll = dialog.querySelector('div:first-child');
body.innerHTML = res.data;
this.$scope.showPreview.show();
scroll.scrollTop = 0;
}
});
}
onSubmit() {
this.$scope.watcher.check();
this.$scope.watcher.realSubmit().then(() =>
this.sendSample()
);
}
sendSample() {
let sampleType = this.$scope.sampleType.selection;
let params = {clientFk: this.$stateParams.id};
if (sampleType.hasCompany)
params.companyFk = this.clientSample.companyFk;
let query = `email/${sampleType.code}?${this.jsonToQuery(params)}`;
this.$http.post(query).then(res => {
if (res) {
this.vnApp.showSuccess(this.$translate.instant('Notification sent!'));
this.$state.go('client.card.sample.index');
}
this.vnApp.showSuccess(this.$translate.instant('Notification sent!'));
this.$state.go('client.card.sample.index');
});
}
}
Controller.$inject = ['$scope', '$state', '$http', 'vnApp', '$translate'];
Controller.$inject = ['$element', '$scope', 'vnApp', '$httpParamSerializer', 'vnConfig'];
ngModule.component('vnClientSampleCreate', {
template: require('./index.html'),
controller: Controller
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -2,14 +2,16 @@ import './index';
describe('Client', () => {
describe('Component vnClientSampleCreate', () => {
let $httpParamSerializer;
let $scope;
let $element;
let $httpBackend;
let $state;
let controller;
beforeEach(ngModule('client'));
beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $rootScope, _$state_) => {
beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $rootScope, _$state_, _$httpParamSerializer_) => {
$scope = $rootScope.$new();
$scope.sampleType = {};
$scope.watcher = {
@ -35,30 +37,24 @@ describe('Client', () => {
$state = _$state_;
$state.params.id = 101;
$httpBackend = _$httpBackend_;
controller = $componentController('vnClientSampleCreate', {$scope, $state});
$httpParamSerializer = _$httpParamSerializer_;
$element = angular.element('<vn-client-sample-create></vn-client-sample-create>');
controller = $componentController('vnClientSampleCreate', {$element, $scope});
}));
describe('jsonToQuery()', () => {
it(`should convert a JSON object with clientFk property to query params`, () => {
let myObject = {clientFk: 101};
let result = controller.jsonToQuery(myObject);
expect(result).toEqual('clientFk=101');
});
it(`should convert a JSON object with clientFk and companyFk properties to query params`, () => {
let myObject = {clientFk: 101, companyFk: 442};
let result = controller.jsonToQuery(myObject);
expect(result).toEqual('clientFk=101&companyFk=442');
});
});
describe('showPreview()', () => {
it(`should perform a query (GET) and open a sample preview`, () => {
spyOn(controller.$scope.showPreview, 'show');
spyOn(controller.$.showPreview, 'show');
const element = document.createElement('div');
document.body.querySelector = () => {
return {
querySelector: () => {
return element;
}
};
};
controller.$scope.sampleType.selection = {
controller.$.sampleType.selection = {
hasCompany: false,
code: 'MyReport'
};
@ -69,18 +65,32 @@ describe('Client', () => {
let event = {preventDefault: () => {}};
$httpBackend.when('GET', `email/MyReport?clientFk=101`).respond(true);
$httpBackend.expect('GET', `email/MyReport?clientFk=101`);
const params = {
clientId: 101,
isPreview: true
};
const serializedParams = $httpParamSerializer(params);
$httpBackend.when('GET', `email/MyReport?${serializedParams}`).respond(true);
$httpBackend.expect('GET', `email/MyReport?${serializedParams}`);
controller.showPreview(event);
$httpBackend.flush();
expect(controller.$scope.showPreview.show).toHaveBeenCalledWith();
expect(controller.$.showPreview.show).toHaveBeenCalledWith();
});
it(`should perform a query (GET) with companyFk param and open a sample preview`, () => {
spyOn(controller.$scope.showPreview, 'show');
spyOn(controller.$.showPreview, 'show');
const element = document.createElement('div');
document.body.querySelector = () => {
return {
querySelector: () => {
return element;
}
};
};
controller.$scope.sampleType.selection = {
controller.$.sampleType.selection = {
hasCompany: true,
code: 'MyReport'
};
@ -92,12 +102,19 @@ describe('Client', () => {
let event = {preventDefault: () => {}};
$httpBackend.when('GET', `email/MyReport?clientFk=101&companyFk=442`).respond(true);
$httpBackend.expect('GET', `email/MyReport?clientFk=101&companyFk=442`);
const params = {
clientId: 101,
companyId: 442,
isPreview: true
};
const serializedParams = $httpParamSerializer(params);
$httpBackend.when('GET', `email/MyReport?${serializedParams}`).respond(true);
$httpBackend.expect('GET', `email/MyReport?${serializedParams}`);
controller.showPreview(event);
$httpBackend.flush();
expect(controller.$scope.showPreview.show).toHaveBeenCalledWith();
expect(controller.$.showPreview.show).toHaveBeenCalledWith();
});
});
@ -114,7 +131,7 @@ describe('Client', () => {
it(`should perform a query (GET) and call go() method`, () => {
spyOn(controller.$state, 'go');
controller.$scope.sampleType.selection = {
controller.$.sampleType.selection = {
hasCompany: false,
code: 'MyReport'
};
@ -123,8 +140,13 @@ describe('Client', () => {
clientFk: 101
};
$httpBackend.when('POST', `email/MyReport?clientFk=101`).respond(true);
$httpBackend.expect('POST', `email/MyReport?clientFk=101`);
const params = {
clientId: 101
};
const serializedParams = $httpParamSerializer(params);
$httpBackend.when('GET', `email/MyReport?${serializedParams}`).respond(true);
$httpBackend.expect('GET', `email/MyReport?${serializedParams}`);
controller.sendSample();
$httpBackend.flush();
@ -134,7 +156,7 @@ describe('Client', () => {
it(`should perform a query (GET) with companyFk param and call go() method`, () => {
spyOn(controller.$state, 'go');
controller.$scope.sampleType.selection = {
controller.$.sampleType.selection = {
hasCompany: true,
code: 'MyReport'
};
@ -144,8 +166,14 @@ describe('Client', () => {
companyFk: 442
};
$httpBackend.when('POST', `email/MyReport?clientFk=101&companyFk=442`).respond(true);
$httpBackend.expect('POST', `email/MyReport?clientFk=101&companyFk=442`);
const params = {
clientId: 101,
companyId: 442
};
const serializedParams = $httpParamSerializer(params);
$httpBackend.when('GET', `email/MyReport?${serializedParams}`).respond(true);
$httpBackend.expect('GET', `email/MyReport?${serializedParams}`);
controller.sendSample();
$httpBackend.flush();

View File

@ -1,36 +1,34 @@
vn-client-sample-create {
vn-dialog {
& > div {
padding: 0 !important
div.vn-dialog {
tpl-body.client-sample-dialog {
width: 800px;
.container, .container h1 {
font-family: "Roboto","Helvetica","Arial",sans-serif;
font-size: 1em !important;
h1 {
font-weight: bold;
margin: auto
}
p {
margin: 1em 0
}
footer p {
font-size: 10px !important;
line-height: 10px
}
}
tpl-body {
min-width: 800px;
.title h1 {
font-size: 2em !important;
margin: 0
}
.container, .container h1 {
font-family: "Roboto","Helvetica","Arial",sans-serif;
font-size: 1em !important;
h1 {
font-weight: bold;
margin: auto
}
p {
margin: 1em 0
}
footer p {
font-size: 10px !important;
line-height: 10px
}
}
.title h1 {
font-size: 2em !important;
margin: 0
}
.loading {
text-align: center
}
}
}
}

View File

@ -42,6 +42,24 @@ export default class Controller {
scope: {
fields: ['id', 'name']
}
},
{
relation: 'worker',
scope: {
fields: ['userFk'],
include: {
relation: 'user',
scope: {
fields: ['id'],
include: {
relation: 'emailUser',
scope: {
fields: ['email']
}
}
}
}
}
}
]
};

View File

@ -1,12 +1,13 @@
import ngModule from '../module';
class Controller {
constructor($, $http, vnApp, $translate, aclService) {
constructor($, $http, vnApp, $translate, aclService, $httpParamSerializer) {
this.$http = $http;
this.vnApp = vnApp;
this.$translate = $translate;
this.$ = $;
this.aclService = aclService;
this.$httpParamSerializer = $httpParamSerializer;
this.moreOptions = [
{callback: this.showRouteReport, name: 'Show route report'},
{callback: this.sendRouteReport, name: 'Send route report'},
@ -36,13 +37,26 @@ class Controller {
}
showRouteReport() {
let url = `report/rpt-route?routeFk=${this.route.id}`;
const user = this.route.worker.user;
const params = {
clientId: user.id,
routeId: this.route.id
};
const serializedParams = this.$httpParamSerializer(params);
let url = `api/report/driver-route?${serializedParams}`;
window.open(url);
}
sendRouteReport() {
let url = `email/driver-route?routeFk=${this.route.id}`;
this.$http.post(url).then(() => {
const user = this.route.worker.user;
const params = {
recipient: user.emailUser.email,
clientId: user.id,
routeId: this.route.id
};
const serializedParams = this.$httpParamSerializer(params);
const url = `email/driver-route?${serializedParams}`;
this.$http.get(url).then(() => {
this.vnApp.showSuccess(this.$translate.instant('Report sent'));
});
}
@ -62,7 +76,7 @@ class Controller {
}
}
Controller.$inject = ['$scope', '$http', 'vnApp', '$translate', 'aclService'];
Controller.$inject = ['$scope', '$http', 'vnApp', '$translate', 'aclService', '$httpParamSerializer'];
ngModule.component('vnRouteDescriptor', {
template: require('./index.html'),

View File

@ -15,7 +15,15 @@ class Controller {
{
relation: 'client',
scope: {
fields: ['salesPersonFk', 'name', 'isActive', 'isFreezed', 'isTaxDataChecked', 'credit'],
fields: [
'salesPersonFk',
'name',
'isActive',
'isFreezed',
'isTaxDataChecked',
'credit',
'email'
],
include: {
relation: 'salesPerson',
scope: {

View File

@ -197,7 +197,7 @@
<vn-confirm
vn-id="confirm-delivery-note"
on-response="$ctrl.sendDeliveryNote($response)"
on-accept="$ctrl.sendDeliveryNote()"
question="Send Delivery Note"
message="Are you sure you want to send it?">
</vn-confirm>

View File

@ -2,9 +2,10 @@ import ngModule from '../module';
import Component from 'core/lib/component';
class Controller extends Component {
constructor($element, $, aclService) {
constructor($element, $, aclService, $httpParamSerializer) {
super($element, $);
this.aclService = aclService;
this.$httpParamSerializer = $httpParamSerializer;
this.moreOptions = [
{name: 'Add turn', callback: this.showAddTurnDialog},
{name: 'Show Delivery Note', callback: this.showDeliveryNote},
@ -198,10 +199,27 @@ class Controller extends Component {
}
showDeliveryNote() {
let url = `report/rpt-delivery-note?ticketFk=${this.ticket.id}`;
const params = {
clientId: this.ticket.client.id,
ticketId: this.ticket.id
};
const serializedParams = this.$httpParamSerializer(params);
let url = `api/report/delivery-note?${serializedParams}`;
window.open(url);
}
sendDeliveryNote() {
const params = {
recipient: this.ticket.client.email,
clientId: this.ticket.client.id,
ticketId: this.ticket.id
};
const serializedParams = this.$httpParamSerializer(params);
this.$http.get(`email/delivery-note?${serializedParams}`).then(
() => this.vnApp.showMessage(this.$translate.instant('Notification sent!'))
);
}
showSMSDialog() {
const address = this.ticket.address;
this.newSMS = {
@ -272,17 +290,9 @@ class Controller extends Component {
confirmDeliveryNote() {
this.$.confirmDeliveryNote.show();
}
sendDeliveryNote(response) {
if (response === 'accept') {
this.$http.post(`email/delivery-note`, {ticketFk: this.ticket.id}).then(
() => this.vnApp.showMessage(this.$translate.instant('Notification sent!'))
);
}
}
}
Controller.$inject = ['$element', '$scope', 'aclService'];
Controller.$inject = ['$element', '$scope', 'aclService', '$httpParamSerializer'];
ngModule.component('vnTicketDescriptor', {
template: require('./index.html'),

View File

@ -1,13 +1,14 @@
import './index.js';
describe('Ticket Component vnTicketDescriptor', () => {
let $httpParamSerializer;
let $httpBackend;
let controller;
let $state;
beforeEach(ngModule('ticket'));
beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $rootScope, $compile, _$state_) => {
beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $rootScope, $compile, _$state_, _$httpParamSerializer_) => {
let $element = $compile(`<vn-autocomplete></vn-autocomplete>`)($rootScope);
$state = _$state_;
$state.getCurrentPath = () => {
@ -17,8 +18,9 @@ describe('Ticket Component vnTicketDescriptor', () => {
];
};
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
controller = $componentController('vnTicketDescriptor', {$element});
controller._ticket = {id: 2, invoiceOut: {id: 1}};
controller._ticket = {id: 2, invoiceOut: {id: 1}, client: {id: 101, email: 'client@email'}};
controller.cardReload = ()=> {
return true;
};
@ -82,7 +84,12 @@ describe('Ticket Component vnTicketDescriptor', () => {
describe('showDeliveryNote()', () => {
it('should open a new window showing a delivery note PDF document', () => {
let expectedPath = 'report/rpt-delivery-note?ticketFk=2';
const params = {
clientId: controller.ticket.client.id,
ticketId: controller.ticket.id
};
const serializedParams = $httpParamSerializer(params);
let expectedPath = `api/report/delivery-note?${serializedParams}`;
spyOn(window, 'open');
controller.showDeliveryNote();
@ -90,6 +97,26 @@ describe('Ticket Component vnTicketDescriptor', () => {
});
});
describe('sendDeliveryNote()', () => {
it('should make a query and call vnApp.showMessage()', () => {
spyOn(controller.vnApp, 'showMessage');
const params = {
recipient: 'client@email',
clientId: controller.ticket.client.id,
ticketId: controller.ticket.id
};
const serializedParams = $httpParamSerializer(params);
$httpBackend.when('GET', `email/delivery-note?${serializedParams}`).respond();
$httpBackend.expect('GET', `email/delivery-note?${serializedParams}`).respond();
controller.sendDeliveryNote();
$httpBackend.flush();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('Notification sent!');
});
});
describe('makeInvoice()', () => {
it('should make a query and call $state.reload() method if the response is accept', () => {
spyOn(controller.$state, 'reload');

2
package-lock.json generated
View File

@ -13500,7 +13500,7 @@
"dependencies": {
"jsesc": {
"version": "0.5.0",
"resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
"dev": true
}

57
print/boot.js Normal file
View File

@ -0,0 +1,57 @@
const express = require('express');
const path = require('path');
const fs = require('fs');
const templatesPath = path.resolve(__dirname, './templates');
const componentsPath = path.resolve(__dirname, './core/components');
module.exports = app => {
global.appPath = __dirname;
process.env.OPENSSL_CONF = '/etc/ssl/';
// Extended locale intl polyfill
const IntlPolyfill = require('intl');
Intl.NumberFormat = IntlPolyfill.NumberFormat;
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;
// Init database instance
require('./core/database').init();
// Init SMTP Instance
require('./core/smtp').init();
//
require('./core/mixins');
require('./core/filters');
require('./core/directives');
// Init router
require('./core/router')(app);
/**
* Serve component static files
*/
const componentsDir = fs.readdirSync(componentsPath);
componentsDir.forEach(componentName => {
const componentDir = path.join(componentsPath, '/', componentName);
const assetsDir = `${componentDir}/assets`;
app.use(`/api/${componentName}/assets`, express.static(assetsDir));
});
/**
* Serve static files
*/
const templatesDir = fs.readdirSync(templatesPath);
templatesDir.forEach(directory => {
const templateTypeDir = path.join(templatesPath, '/', directory);
const templates = fs.readdirSync(templateTypeDir);
templates.forEach(templateName => {
const templateDir = path.join(templatesPath, '/', directory, '/', templateName);
const assetsDir = `${templateDir}/assets`;
app.use(`/api/${templateName}/assets`, express.static(assetsDir));
});
});
};

View File

@ -1,40 +1,30 @@
/**
* Email only stylesheet
*
*/
body {
background-color: #EEE;
color: #555;
margin: 0
}
.grid {
background-color: #EEE
}
.container {
max-width: 600px;
min-width: 320px;
margin: 0 auto;
color: #555
}
.main {
background-color: #FFF;
padding: 20px
}
.main a {
.grid a {
color: #8dba25
}
.main h1 {
color: #999
.grid-block {
min-width: 320px;
max-width: 600px;
margin: 0 auto;
width: 600px
}
.main h3 {
font-size: 16px
}
.title {
background-color: #95d831;
text-transform: uppercase;
text-align: center;
padding: 35px 0
}
.title h1 {
font-size: 32px;
h1 {
font-weight: 100;
font-size: 1.5em;
color: #333;
margin: 0
}

View File

@ -1,10 +1,34 @@
.container {
/**
* CSS layout elements
*
*/
.grid {
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
font-size: 16px
font-size: 16px;
width: 100%
}
.grid-row {
background-color: transparent
}
.grid-block {
box-sizing: border-box;
min-height: 40px
}
.grid-block.empty {
height: 40px
}
.grid-block.white {
background-color: #FFF
}
.columns {
overflow: hidden
overflow: hidden;
box-sizing: border-box;
}
.columns .size100 {
@ -18,6 +42,7 @@
}
.columns .size50 {
box-sizing: border-box;
width: 50%;
float: left
}
@ -173,7 +198,7 @@ table {
}
.panel .row-oriented td, .panel .row-oriented th {
padding: 10px 0
padding: 8px 10px
}
.row-oriented > tbody > tr > td {
@ -199,8 +224,8 @@ table {
margin-left: -1px;
margin-right: 1px;
margin-top: 10px;
padding: 5px 0;
color: #999;
padding: 5px 0
}
.line .vertical-aligned {

View File

@ -1,3 +1,7 @@
/**
* CSS misc classes
*
*/
.uppercase {
text-transform: uppercase
}

View File

@ -1,5 +1,9 @@
/**
* Report only stylesheet
*
*/
body {
zoom: 0.55
zoom: 0.53
}
.title {

View File

@ -0,0 +1,349 @@
/**
* CSS spacing classes
*
* vn-[p|m][t|r|b|l|a|x|y]-[none|auto|xs|sm|md|lg|xl]
* T D S
*
* T - type
* - values: p (padding), m (margin)
*
* D - direction
* - values:
* t (top), r (right), b (bottom), l (left),
* a (all), x (both left & right), y (both top & bottom)
*
* S - size
* - values:
* none,
* auto (ONLY for specific margins: vn-ml-*, vn-mr-*, vn-mx-*),
* xs (extra small),
* sm (small),
* md (medium),
* lg (large),
* xl (extra large)
*/
/* ++++++++++++++++++++++++++++++++++++++++++++++++ Padding */
.vn-pa-none {
padding: 0;
}
.vn-pl-none {
padding-left: 0;
}
.vn-pr-none {
padding-right: 0;
}
.vn-pt-none {
padding-top: 0;
}
.vn-pb-none {
padding-bottom: 0;
}
.vn-py-none {
padding-top: 0;
padding-bottom: 0;
}
.vn-px-none {
padding-left: 0;
padding-right: 0;
}
.vn-pa-xs {
padding: 4px;
}
.vn-pl-xs {
padding-left: 4px;
}
.vn-pr-xs {
padding-right: 4px;
}
.vn-pt-xs {
padding-top: 4px;
}
.vn-pb-xs {
padding-bottom: 4px;
}
.vn-py-xs {
padding-top: 4px;
padding-bottom: 4px;
}
.vn-px-xs {
padding-left: 4px;
padding-right: 4px;
}
/* Small */
.vn-pa-sm {
padding: 8px;
}
.vn-pl-sm {
padding-left: 8px;
}
.vn-pr-sm {
padding-right: 8px;
}
.vn-pt-sm {
padding-top: 8px;
}
.vn-pb-sm {
padding-bottom: 8px;
}
.vn-py-sm {
padding-top: 8px;
padding-bottom: 8px;
}
.vn-px-sm {
padding-left: 8px;
padding-right: 8px;
}
/* Medium */
.vn-pa-md {
padding: 16px;
}
.vn-pl-md {
padding-left: 16px;
}
.vn-pr-md {
padding-right: 16px;
}
.vn-pt-md {
padding-top: 16px;
}
.vn-pb-md {
padding-bottom: 16px;
}
.vn-py-md {
padding-top: 16px;
padding-bottom: 16px;
}
.vn-px-md {
padding-left: 16px;
padding-right: 16px;
}
/* Large */
.vn-pa-lg {
padding: 32px;
}
.vn-pl-lg {
padding-left: 32px;
}
.vn-pr-lg {
padding-right: 32px;
}
.vn-pt-lg {
padding-top: 32px;
}
.vn-pb-lg {
padding-bottom: 32px;
}
.vn-py-lg {
padding-top: 32px;
padding-bottom: 32px;
}
.vn-px-lg {
padding-left: 32px;
padding-right: 32px;
}
/* Extra large */
.vn-pa-xl {
padding: 100px;
}
.vn-pl-xl {
padding-left: 100px;
}
.vn-pr-xl {
padding-right: 100px;
}
.vn-pt-xl {
padding-top: 100px;
}
.vn-pb-xl {
padding-bottom: 100px;
}
.vn-py-xl {
padding-top: 100px;
padding-bottom: 100px;
}
.vn-px-xl {
padding-left: 100px;
padding-right: 100px;
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++ Margin */
/* None */
.vn-ma-none {
padding: 0;
}
.vn-ml-none {
padding-left: 0;
}
.vn-mr-none {
padding-right: 0;
}
.vn-mt-none {
padding-top: 0;
}
.vn-mb-none {
padding-bottom: 0;
}
.vn-my-none {
padding-top: 0;
padding-bottom: 0;
}
.vn-mx-none {
padding-left: 0;
padding-right: 0;
}
/* Auto */
.vn-ml-none {
padding-left: auto;
}
.vn-mr-none {
padding-right: auto;
}
.vn-mx-none {
padding-left: auto;
padding-right: auto;
}
/* Extra small */
.vn-ma-xs {
margin: 4px;
}
.vn-mt-xs {
margin-top: 4px;
}
.vn-ml-xs {
margin-left: 4px;
}
.vn-mr-xs {
margin-right: 4px;
}
.vn-mb-xs {
margin-bottom: 4px;
}
.vn-my-xs {
margin-top: 4px;
margin-bottom: 4px;
}
.vn-mx-xs {
margin-left: 4px;
margin-right: 4px;
}
/* Small */
.vn-ma-sm {
margin: 8px;
}
.vn-mt-sm {
margin-top: 8px;
}
.vn-ml-sm {
margin-left: 8px;
}
.vn-mr-sm {
margin-right: 8px;
}
.vn-mb-sm {
margin-bottom: 8px;
}
.vn-my-sm {
margin-top: 8px;
margin-bottom: 8px;
}
.vn-mx-sm {
margin-left: 8px;
margin-right: 8px;
}
/* Medium */
.vn-ma-md {
margin: 16px;
}
.vn-mt-md {
margin-top: 16px;
}
.vn-ml-md {
margin-left: 16px;
}
.vn-mr-md {
margin-right: 16px;
}
.vn-mb-md {
margin-bottom: 16px;
}
.vn-my-md {
margin-top: 16px;
margin-bottom: 16px;
}
.vn-mx-md {
margin-left: 16px;
margin-right: 16px;
}
/* Large */
.vn-ma-lg {
margin: 32px;
}
.vn-mt-lg {
margin-top: 32px;
}
.vn-ml-lg {
margin-left: 32px;
}
.vn-mr-lg {
margin-right: 32px;
}
.vn-mb-lg {
margin-bottom: 32px;
}
.vn-my-lg {
margin-top: 32px;
margin-bottom: 32px;
}
.vn-mx-lg {
margin-left: 32px;
margin-right: 32px;
}
/* Extra large */
.vn-ma-xl {
margin: 100px;
}
.vn-mt-xl {
margin-top: 100px;
}
.vn-ml-xl {
margin-left: 100px;
}
.vn-mr-xl {
margin-right: 100px;
}
.vn-mb-xl {
margin-bottom: 100px;
}
.vn-my-xl {
margin-top: 100px;
margin-bottom: 100px;
}
.vn-mx-xl {
margin-left: 100px;
margin-right: 100px;
}

View File

@ -1,10 +1,15 @@
{
"app": {
"host": "http://localhost:5000",
"port": 3000,
"defaultLanguage": "es",
"senderMail": "nocontestar@verdnatura.es",
"senderName": "Verdnatura"
},
"i18n": {
"locale": "es",
"fallbackLocale": "es",
"silentTranslationWarn": true
},
"pdf": {
"format": "A4",
"border": "1.5cm",

View File

@ -1,25 +0,0 @@
[
{"type": "email", "name": "client-welcome"},
{"type": "email", "name": "printer-setup"},
{"type": "email", "name": "payment-update"},
{"type": "email", "name": "letter-debtor-st"},
{"type": "email", "name": "letter-debtor-nd"},
{"type": "email", "name": "claim-pickup-order"},
{"type": "email", "name": "sepa-core"},
{"type": "email", "name": "client-lcr"},
{"type": "email", "name": "driver-route"},
{"type": "email", "name": "delivery-note"},
{"type": "report", "name": "rpt-delivery-note"},
{"type": "report", "name": "rpt-claim-pickup-order"},
{"type": "report", "name": "rpt-letter-debtor"},
{"type": "report", "name": "rpt-sepa-core"},
{"type": "report", "name": "rpt-receipt"},
{"type": "report", "name": "rpt-zone"},
{"type": "report", "name": "rpt-route"},
{"type": "report", "name": "rpt-lcr"},
{"type": "report", "name": "rpt-item-label"},
{"type": "static", "name": "email-header"},
{"type": "static", "name": "email-footer"},
{"type": "static", "name": "report-header"},
{"type": "static", "name": "report-footer"}
]

104
print/core/component.js Normal file
View File

@ -0,0 +1,104 @@
const Vue = require('vue');
const VueI18n = require('vue-i18n');
const renderer = require('vue-server-renderer').createRenderer();
Vue.use(VueI18n);
const fs = require('fs');
const yaml = require('js-yaml');
const juice = require('juice');
const path = require('path');
const config = require('./config');
class Component {
constructor(name) {
this.name = name;
}
get path() {
return `./components/${this.name}`;
}
get template() {
const templatePath = `${this.path}/${this.name}.html`;
const fullPath = path.resolve(__dirname, templatePath);
return fs.readFileSync(fullPath, 'utf8');
}
get locale() {
if (!this._locale)
this.getLocale();
return this._locale;
}
getLocale() {
const mergedLocale = {messages: {}};
const localePath = path.resolve(__dirname, `${this.path}/locale`);
if (!fs.existsSync(localePath))
return mergedLocale;
const localeDir = fs.readdirSync(localePath);
localeDir.forEach(locale => {
const fullPath = path.join(localePath, '/', locale);
const yamlLocale = fs.readFileSync(fullPath, 'utf8');
const jsonLocale = yaml.safeLoad(yamlLocale);
const localeName = locale.replace('.yml', '');
mergedLocale.messages[localeName] = jsonLocale;
});
this._locale = mergedLocale;
}
get stylesheet() {
let mergedStyles = '';
const stylePath = path.resolve(__dirname, `${this.path}/assets/css`);
if (!fs.existsSync(stylePath))
return mergedStyles;
return require(`${stylePath}/import`);
}
get attachments() {
const attachmentsPath = `${this.path}/attachments.json`;
const fullPath = path.resolve(__dirname, attachmentsPath);
if (!fs.existsSync(fullPath))
return [];
return require(fullPath);
}
build() {
const fullPath = path.resolve(__dirname, this.path);
if (!fs.existsSync(fullPath))
throw new Error(`Sample "${this.name}" not found`);
const component = require(`${this.path}/${this.name}`);
component.i18n = this.locale;
component.attachments = this.attachments;
component.template = juice.inlineContent(this.template, this.stylesheet, {
inlinePseudoElements: true
});
return component;
}
async render() {
const component = this.build();
const i18n = new VueI18n(config.i18n);
const app = new Vue({
i18n: i18n,
render: h => h(component, {
props: this.args
})
});
return renderer.renderToString(app);
}
}
module.exports = Component;

View File

@ -0,0 +1,9 @@
const Stylesheet = require(`${appPath}/core/stylesheet`);
module.exports = new Stylesheet([
`${appPath}/common/css/spacing.css`,
`${appPath}/common/css/misc.css`,
`${appPath}/common/css/layout.css`,
`${appPath}/common/css/email.css`,
`${__dirname}/style.css`])
.mergeStyles();

View File

@ -0,0 +1,22 @@
div {
display: inline-block;
box-sizing: border-box
}
a {
background-color: #F5F5F5;
border: 1px solid #CCC;
display: flex;
vertical-align: middle;
box-sizing: border-box;
min-width: 150px;
text-decoration: none;
border-radius: 3px;
color: #8dba25
}
a > div.icon {
font-weight: bold;
font-size: 18px;
color: #555
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M2 12.5C2 9.46 4.46 7 7.5 7H18c2.21 0 4 1.79 4 4s-1.79 4-4 4H9.5C8.12 15 7 13.88 7 12.5S8.12 10 9.5 10H17v2H9.41c-.55 0-.55 1 0 1H18c1.1 0 2-.9 2-2s-.9-2-2-2H7.5C5.57 9 4 10.57 4 12.5S5.57 16 7.5 16H17v2H7.5C4.46 18 2 15.54 2 12.5z"/><path fill="none" d="M0 0h24v24H0V0z"/></svg>

After

Width:  |  Height:  |  Size: 371 B

View File

@ -0,0 +1,6 @@
<div class="vn-mx-xs" v-if="attachment.component">
<a target="_blank" class="vn-py-sm vn-px-md" v-bind:href="path">
<div class="text">{{attachment.filename}}</div>
<div class="icon vn-pl-md">&#x25BC;</div>
</a>
</div>

View File

@ -0,0 +1,37 @@
module.exports = {
name: 'attachment',
computed: {
path() {
const filename = this.attachment.filename;
const component = this.attachment.component;
if (this.attachment.cid)
return `/api/${component}/assets/files/${filename}`;
else
return `/api/report/${component}?${this.getHttpParams()}`;
}
},
methods: {
getHttpParams() {
const props = this.args;
let query = '';
for (let param in props) {
if (query != '')
query += '&';
query += `${param}=${props[param]}`;
}
return query;
}
},
props: {
attachment: {
type: Object,
required: true
},
args: {
type: Object,
required: false
}
}
};

View File

@ -0,0 +1,9 @@
const Stylesheet = require(`${appPath}/core/stylesheet`);
module.exports = new Stylesheet([
`${appPath}/common/css/spacing.css`,
`${appPath}/common/css/misc.css`,
`${appPath}/common/css/layout.css`,
`${appPath}/common/css/email.css`,
`${__dirname}/style.css`])
.mergeStyles();

View File

@ -1,21 +1,11 @@
@media (max-width: 400px) {
.buttons a {
display: block;
width: 100%
}
}
.buttons {
width: 100%
}
.buttons a {
display: inline-block;
box-sizing: border-box;
text-decoration: none;
font-size: 16px;
width: 100%;
color: #fff;
width: 50%
}
.buttons .btn {
@ -23,18 +13,20 @@
text-align: center
}
.buttons .btn .text {
display: inline-block;
padding: 22px 0
}
.buttons .btn .icon {
background-color: #95d831;
box-sizing: border-box;
font-weight: bold;
text-align: center;
padding: 16.5px 0;
float: right;
width: 70px
color: #333;
float: left
}
.buttons .btn .text {
display: inline-block;
}
.networks {

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,32 @@
[
{
"filename": "facebook.png",
"path": "/assets/images/facebook.png",
"cid": "facebook.png"
},
{
"filename": "twitter.png",
"path": "/assets/images/twitter.png",
"cid": "twitter.png"
},
{
"filename": "youtube.png",
"path": "/assets/images/youtube.png",
"cid": "youtube.png"
},
{
"filename": "pinterest.png",
"path": "/assets/images/pinterest.png",
"cid": "pinterest.png"
},
{
"filename": "instagram.png",
"path": "/assets/images/instagram.png",
"cid": "instagram.png"
},
{
"filename": "linkedin.png",
"path": "/assets/images/linkedin.png",
"cid": "linkedin.png"
}
]

View File

@ -0,0 +1,53 @@
<footer>
<!-- Action button block -->
<div class="buttons">
<div class="columns">
<div class="size50">
<a href="https://www.verdnatura.es" target="_blank">
<div class="btn">
<!-- <span class="icon vn-pa-sm"><img v-bind:src="getEmailSrc('action.png')"/></span> -->
<span class="text vn-pa-sm">{{ $t('buttons.webAcccess')}}</span>
</div>
</a>
</div>
<div class="size50">
<a href="https://goo.gl/forms/j8WSL151ZW6QtlT72" target="_blank">
<div class="btn">
<!-- <span class="icon vn-pa-sm"><img v-bind:src="getEmailSrc('info.png')"/></span> -->
<span class="text vn-pa-sm">{{ $t('buttons.info')}}</span>
</div>
</a>
</div>
</div>
</div>
<!-- Networks block -->
<div class="networks">
<a href="https://www.facebook.com/Verdnatura" target="_blank">
<img v-bind:src="getEmailSrc('facebook.png')" alt="Facebook"/>
</a>
<a href="https://www.twitter.com/Verdnatura" target="_blank">
<img v-bind:src="getEmailSrc('twitter.png')" alt="Twitter"/>
</a>
<a href="https://www.youtube.com/Verdnatura" target="_blank">
<img v-bind:src="getEmailSrc('youtube.png')" alt="Youtube"/>
</a>
<a href="https://www.pinterest.com/Verdnatura" target="_blank">
<img v-bind:src="getEmailSrc('pinterest.png')" alt="Pinterest"/>
</a>
<a href="https://www.instagram.com/Verdnatura" target="_blank">
<img v-bind:src="getEmailSrc('instagram.png')" alt="Instagram"/>
</a>
<a href="https://www.linkedin.com/company/verdnatura" target="_blank">
<img v-bind:src="getEmailSrc('linkedin.png')" alt="Linkedin"/>
</a>
</div>
<!-- Privacy block -->
<div class="privacy">
<p>{{$t('privacy.fiscalAddress')}}</p>
<p>{{$t('privacy.disclaimer')}}</p>
<p>{{$t('privacy.law')}}</p>
</div>
<!-- Privacy block end -->
</footer>

View File

@ -0,0 +1,4 @@
module.exports = {
name: 'email-footer',
props: ['isPreview', 'locale']
};

View File

@ -0,0 +1,19 @@
buttons:
webAcccess: Visita nuestra Web
info: Ayúdanos a mejorar
privacy:
fiscalAddress: VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla
· www.verdnatura.es · clientes@verdnatura.es
disclaimer: '- AVISO - Este mensaje es privado y confidencial, y debe ser utilizado
exclusivamente por la persona destinataria del mismo. Si has recibido este mensaje
por error, te rogamos lo comuniques al remitente y borres dicho mensaje y cualquier
documento adjunto que pudiera contener. Verdnatura Levante SL no renuncia a la
confidencialidad ni a ningún privilegio por causa de transmisión errónea o mal
funcionamiento. Igualmente no se hace responsable de los cambios, alteraciones,
errores u omisiones que pudieran hacerse al mensaje una vez enviado.'
law: En cumplimiento de lo dispuesto en la Ley Orgánica 15/1999, de Protección de
Datos de Carácter Personal, te comunicamos que los datos personales que facilites
se incluirán en ficheros automatizados de VERDNATURA LEVANTE S.L., pudiendo en
todo momento ejercitar los derechos de acceso, rectificación, cancelación y oposición,
comunicándolo por escrito al domicilio social de la entidad. La finalidad del
fichero es la gestión administrativa, contabilidad, y facturación.

View File

@ -0,0 +1,19 @@
buttons:
webAcccess: Visitez notre site web
info: Aidez-nous à améliorer
privacy:
fiscalAddress: VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla
· www.verdnatura.es · clientes@verdnatura.es
disclaimer: '- AVIS - Ce message est privé et confidentiel et doit être utilisé.exclusivamente
por la persona destinataria del mismo. Si has recibido este mensajepor error,
te rogamos lo comuniques al remitente y borres dicho mensaje y cualquier documentoadjunto
que pudiera contener. Verdnatura Levante SL no renuncia a la confidencialidad
ni aningún privilegio por causa de transmisión errónea o mal funcionamiento. Igualmente
no se haceresponsable de los cambios, alteraciones, errores u omisiones que pudieran
hacerse al mensaje una vez enviado.'
law: En cumplimiento de lo dispuesto en la Ley Orgánica 15/1999, de Protección de
Datos de Carácter Personal, te comunicamos que los datos personales que facilites
se incluirán en ficheros automatizados de VERDNATURA LEVANTE S.L.,pudiendo en
todo momento ejercitar los derechos de acceso, rectificación, cancelación y oposición,
comunicándolo porescrito al domicilio social de la entidad. La finalidad del fichero
es la gestión administrativa, contabilidad, y facturación.

View File

@ -0,0 +1,9 @@
const Stylesheet = require(`${appPath}/core/stylesheet`);
module.exports = new Stylesheet([
`${appPath}/common/css/spacing.css`,
`${appPath}/common/css/misc.css`,
`${appPath}/common/css/layout.css`,
`${appPath}/common/css/email.css`,
`${__dirname}/style.css`])
.mergeStyles();

View File

@ -0,0 +1,19 @@
header .logo {
margin-bottom: 15px;
}
header .logo img {
width: 50%
}
header .topbar {
background-color: #95d831;
height: 25px
}
.topbar:after {
overflow: hidden;
display: block;
content: ' ';
clear: both;
}

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
width="500"
height="68.596313"
viewBox="0 0 499.99999 68.596313"
enable-background="new 0 0 226.229 31.038"
xml:space="preserve"
id="svg2"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="verdnatura-white.svg"><metadata
id="metadata61"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs59" /><sodipodi:namedview
pagecolor="#333333"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1013"
id="namedview57"
showgrid="false"
inkscape:zoom="1.5909426"
inkscape:cx="268.25598"
inkscape:cy="112.75218"
inkscape:window-x="0"
inkscape:window-y="30"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" /><g
id="Background"
transform="translate(2.2478643e-6,43.261169)" /><g
id="Guides"
transform="translate(2.2478643e-6,43.261169)" /><g
id="g883"
transform="matrix(2.2101465,0,0,2.2101465,0,-594.44542)"><g
transform="translate(0,268.962)"
style="fill:#ffffff"
id="g9"><path
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd"
inkscape:connector-curvature="0"
id="path11"
d="M 10.417,30.321 0,0 h 8.233 l 4.26,15.582 0.349,1.276 c 0.521,1.866 0.918,3.431 1.191,4.693 0.15,-0.618 0.335,-1.345 0.555,-2.182 0.219,-0.837 0.528,-1.935 0.925,-3.293 L 19.981,0 h 8.19 l -10.5,30.321 z" /></g><g
transform="translate(0,268.962)"
id="g13"><path
inkscape:connector-curvature="0"
style="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
id="path15"
d="m 139.809,19.787 c -0.665,0.357 -1.748,0.686 -3.25,0.988 -0.727,0.137 -1.283,0.254 -1.667,0.35 -0.95,0.247 -1.661,0.563 -2.134,0.947 -0.472,0.384 -0.799,0.899 -0.979,1.544 -0.223,0.796 -0.155,1.438 0.204,1.925 0.359,0.488 0.945,0.731 1.757,0.731 1.252,0 2.375,-0.36 3.369,-1.081 0.994,-0.721 1.653,-1.665 1.98,-2.831 z m 5.106,10.534 h -7.458 c 0.017,-0.356 0.048,-0.726 0.094,-1.11 l 0.159,-1.192 c -1.318,1.026 -2.627,1.786 -3.927,2.279 -1.299,0.493 -2.643,0.739 -4.031,0.739 -2.158,0 -3.7,-0.593 -4.625,-1.779 -0.925,-1.187 -1.106,-2.788 -0.542,-4.804 0.519,-1.851 1.431,-3.356 2.737,-4.515 1.307,-1.159 3.021,-1.972 5.142,-2.438 1.169,-0.247 2.641,-0.515 4.413,-0.803 2.646,-0.412 4.082,-1.016 4.304,-1.812 l 0.151,-0.539 c 0.182,-0.65 0.076,-1.145 -0.317,-1.483 -0.393,-0.339 -1.071,-0.508 -2.033,-0.508 -1.045,0 -1.934,0.214 -2.666,0.643 -0.731,0.428 -1.289,1.058 -1.673,1.887 h -6.748 c 1.065,-2.53 2.64,-4.413 4.723,-5.65 2.083,-1.237 4.724,-1.856 7.923,-1.856 1.991,0 3.602,0.241 4.833,0.722 1.231,0.481 2.095,1.209 2.59,2.185 0.339,0.701 0.483,1.536 0.432,2.504 -0.052,0.969 -0.377,2.525 -0.978,4.669 l -2.375,8.483 c -0.284,1.014 -0.416,1.812 -0.396,2.395 0.02,0.583 0.188,0.962 0.503,1.141 z" /></g><g
transform="translate(0,268.962)"
id="g17"><path
inkscape:connector-curvature="0"
style="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
id="path19"
d="m 185.7,30.321 6.27,-22.393 h 7.049 l -1.097,3.918 c 1.213,-1.537 2.502,-2.659 3.867,-3.366 1.365,-0.707 2.951,-1.074 4.758,-1.101 l -2.03,7.25 c -0.304,-0.042 -0.608,-0.072 -0.912,-0.093 -0.303,-0.02 -0.592,-0.03 -0.867,-0.03 -1.126,0 -2.104,0.168 -2.932,0.504 -0.829,0.336 -1.561,0.854 -2.197,1.555 -0.406,0.467 -0.789,1.136 -1.149,2.007 -0.361,0.872 -0.814,2.282 -1.359,4.232 l -2.104,7.516 H 185.7 Z" /></g><g
transform="translate(0,268.962)"
id="g21"><path
inkscape:connector-curvature="0"
style="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
id="path23"
d="m 217.631,19.787 c -0.664,0.357 -1.748,0.686 -3.25,0.988 -0.727,0.137 -1.282,0.254 -1.667,0.35 -0.95,0.247 -1.661,0.563 -2.134,0.947 -0.472,0.384 -0.799,0.899 -0.979,1.544 -0.223,0.796 -0.155,1.438 0.205,1.925 0.359,0.488 0.945,0.731 1.757,0.731 1.252,0 2.375,-0.36 3.369,-1.081 0.994,-0.721 1.654,-1.665 1.98,-2.831 z m 5.106,10.534 h -7.458 c 0.017,-0.356 0.048,-0.726 0.094,-1.11 l 0.159,-1.192 c -1.318,1.026 -2.627,1.786 -3.927,2.279 -1.299,0.493 -2.643,0.739 -4.031,0.739 -2.158,0 -3.7,-0.593 -4.625,-1.779 -0.926,-1.187 -1.106,-2.788 -0.542,-4.804 0.519,-1.851 1.431,-3.356 2.737,-4.515 1.306,-1.159 3.02,-1.972 5.142,-2.438 1.169,-0.247 2.641,-0.515 4.413,-0.803 2.647,-0.412 4.082,-1.016 4.304,-1.812 l 0.151,-0.539 c 0.182,-0.65 0.077,-1.145 -0.317,-1.483 -0.393,-0.339 -1.071,-0.508 -2.033,-0.508 -1.045,0 -1.934,0.214 -2.666,0.643 -0.731,0.428 -1.289,1.058 -1.672,1.887 h -6.748 c 1.065,-2.53 2.64,-4.413 4.723,-5.65 2.083,-1.237 4.724,-1.856 7.923,-1.856 1.99,0 3.601,0.241 4.833,0.722 1.232,0.481 2.095,1.209 2.591,2.185 0.339,0.701 0.483,1.536 0.431,2.504 -0.051,0.969 -0.377,2.525 -0.978,4.669 l -2.375,8.483 c -0.284,1.014 -0.416,1.812 -0.396,2.395 0.02,0.583 0.188,0.962 0.503,1.141 z" /></g><g
transform="translate(0,268.962)"
id="g25"><path
inkscape:connector-curvature="0"
style="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
id="path27"
d="m 188.386,7.928 -6.269,22.393 h -7.174 l 0.864,-3.085 c -1.227,1.246 -2.476,2.163 -3.746,2.751 -1.27,0.588 -2.625,0.882 -4.067,0.882 -2.471,0 -4.154,-0.634 -5.048,-1.901 -0.895,-1.268 -0.993,-3.149 -0.294,-5.644 l 4.31,-15.396 h 7.338 l -3.508,12.53 c -0.516,1.842 -0.641,3.109 -0.375,3.803 0.266,0.694 0.967,1.041 2.105,1.041 1.275,0 2.323,-0.422 3.142,-1.267 0.819,-0.845 1.497,-2.223 2.031,-4.133 l 3.353,-11.974 z" /></g><g
transform="translate(0,268.962)"
id="g29"><path
inkscape:connector-curvature="0"
style="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
id="path31"
d="m 149.937,12.356 1.239,-4.428 h 2.995 l 1.771,-6.326 h 7.338 l -1.771,6.326 h 3.753 l -1.24,4.428 h -3.753 l -2.716,9.702 c -0.416,1.483 -0.498,2.465 -0.247,2.946 0.25,0.48 0.905,0.721 1.964,0.721 l 0.549,-0.011 0.39,-0.031 -1.31,4.678 c -0.811,0.148 -1.596,0.263 -2.354,0.344 -0.758,0.081 -1.48,0.122 -2.167,0.122 -2.543,0 -4.108,-0.621 -4.695,-1.863 -0.587,-1.242 -0.313,-3.887 0.82,-7.936 l 2.428,-8.672 z" /></g><g
transform="translate(0,268.962)"
style="fill:#ffffff"
id="g33"><path
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd"
inkscape:connector-curvature="0"
id="path35"
d="m 73.875,18.896 c -0.561,2.004 -0.616,3.537 -0.167,4.601 0.449,1.064 1.375,1.595 2.774,1.595 1.399,0 2.605,-0.524 3.62,-1.574 1.015,-1.05 1.806,-2.59 2.375,-4.622 0.526,-1.879 0.556,-3.334 0.09,-4.363 -0.466,-1.029 -1.393,-1.543 -2.778,-1.543 -1.304,0 -2.487,0.528 -3.551,1.585 -1.064,1.057 -1.852,2.496 -2.363,4.321 z M 96.513,0 88.024,30.321 h -7.337 l 0.824,-2.944 c -1.166,1.22 -2.369,2.121 -3.61,2.703 -1.241,0.582 -2.583,0.874 -4.025,0.874 -2.802,0 -4.772,-1.081 -5.912,-3.243 -1.139,-2.162 -1.218,-4.993 -0.238,-8.493 0.988,-3.528 2.668,-6.404 5.042,-8.627 2.374,-2.224 4.927,-3.336 7.661,-3.336 1.47,0 2.695,0.296 3.676,0.887 0.981,0.591 1.681,1.465 2.099,2.62 L 89.217,0 Z" /><g
style="fill:#ffffff"
id="g37"><path
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd"
inkscape:connector-curvature="0"
id="path39"
d="m 73.875,18.896 c -0.561,2.004 -0.616,3.537 -0.167,4.601 0.449,1.064 1.375,1.595 2.774,1.595 1.399,0 2.605,-0.524 3.62,-1.574 1.015,-1.05 1.806,-2.59 2.375,-4.622 0.526,-1.879 0.556,-3.334 0.09,-4.363 -0.466,-1.029 -1.393,-1.543 -2.778,-1.543 -1.304,0 -2.487,0.528 -3.551,1.585 -1.064,1.057 -1.852,2.496 -2.363,4.321 z M 96.513,0 88.024,30.321 h -7.337 l 0.824,-2.944 c -1.166,1.22 -2.369,2.121 -3.61,2.703 -1.241,0.582 -2.583,0.874 -4.025,0.874 -2.802,0 -4.772,-1.081 -5.912,-3.243 -1.139,-2.162 -1.218,-4.993 -0.238,-8.493 0.988,-3.528 2.668,-6.404 5.042,-8.627 2.374,-2.224 4.927,-3.336 7.661,-3.336 1.47,0 2.695,0.296 3.676,0.887 0.981,0.591 1.681,1.465 2.099,2.62 L 89.217,0 Z" /></g></g><g
transform="translate(0,268.962)"
style="fill:#ffffff"
id="g41"><path
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd"
inkscape:connector-curvature="0"
id="path43"
d="M 46.488,30.321 52.757,7.928 h 7.049 l -1.098,3.918 C 59.921,10.309 61.21,9.187 62.576,8.48 63.942,7.773 68.591,7.406 70.398,7.379 l -2.03,7.25 c -0.304,-0.042 -0.608,-0.072 -0.911,-0.093 -0.304,-0.02 -0.592,-0.03 -0.867,-0.03 -1.126,0 -5.167,0.168 -5.997,0.504 -0.829,0.336 -1.561,0.854 -2.196,1.555 -0.406,0.467 -0.789,1.136 -1.149,2.007 -0.361,0.872 -0.814,2.282 -1.36,4.232 l -2.104,7.516 h -7.296 z" /></g><g
transform="translate(0,268.962)"
style="fill:#ffffff"
id="g45"><path
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd"
inkscape:connector-curvature="0"
id="path47"
d="m 32.673,16.742 8.351,-0.021 c 0.375,-1.436 0.308,-2.558 -0.201,-3.365 -0.509,-0.807 -1.402,-1.211 -2.68,-1.211 -1.209,0 -2.285,0.397 -3.229,1.19 -0.944,0.793 -1.69,1.93 -2.241,3.407 z m 6.144,6.536 h 7.043 c -1.347,2.456 -3.172,4.356 -5.477,5.7 -2.305,1.345 -4.885,2.017 -7.74,2.017 -3.473,0 -5.923,-1.054 -7.351,-3.161 -1.427,-2.107 -1.632,-4.98 -0.613,-8.618 1.038,-3.707 2.875,-6.641 5.512,-8.803 2.637,-2.163 5.678,-3.244 9.123,-3.244 3.555,0 6.04,1.099 7.456,3.298 1.417,2.198 1.582,5.234 0.498,9.109 l -0.239,0.814 -0.167,0.484 H 31.721 c -0.441,1.575 -0.438,2.777 0.01,3.606 0.448,0.829 1.332,1.244 2.65,1.244 0.975,0 1.836,-0.206 2.583,-0.617 0.747,-0.411 1.366,-1.021 1.853,-1.829 z" /><g
style="fill:#ffffff"
id="g49"><path
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd"
inkscape:connector-curvature="0"
id="path51"
d="m 32.673,16.742 8.351,-0.021 c 0.375,-1.436 0.308,-2.558 -0.201,-3.365 -0.509,-0.807 -1.402,-1.211 -2.68,-1.211 -1.209,0 -2.285,0.397 -3.229,1.19 -0.944,0.793 -1.69,1.93 -2.241,3.407 z m 6.144,6.536 h 7.043 c -1.347,2.456 -3.172,4.356 -5.477,5.7 -2.305,1.345 -4.885,2.017 -7.74,2.017 -3.473,0 -5.923,-1.054 -7.351,-3.161 -1.427,-2.107 -1.632,-4.98 -0.613,-8.618 1.038,-3.707 2.875,-6.641 5.512,-8.803 2.637,-2.163 5.678,-3.244 9.123,-3.244 3.555,0 6.04,1.099 7.456,3.298 1.417,2.198 1.582,5.234 0.498,9.109 l -0.239,0.814 -0.167,0.484 H 31.721 c -0.441,1.575 -0.438,2.777 0.01,3.606 0.448,0.829 1.332,1.244 2.65,1.244 0.975,0 1.836,-0.206 2.583,-0.617 0.747,-0.411 1.366,-1.021 1.853,-1.829 z" /></g></g><g
transform="translate(0,268.962)"
id="g53"><path
inkscape:connector-curvature="0"
style="fill:#8ed300;fill-opacity:1"
id="path55"
d="m 112.881,30.643 -6.404,-18.639 -6.455,18.639 h -7.254 l 9.565,-30.321 h 8.19 l 4.434,15.582 0.35,1.276 c 0.521,1.866 0.917,3.431 1.191,4.693 l 0.555,-2.182 c 0.219,-0.837 0.528,-1.935 0.925,-3.293 l 4.468,-16.076 h 8.19 l -10.501,30.321 z" /></g></g></svg>

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,7 @@
[
{
"filename": "logo-black.png",
"path": "/assets/images/logo-black.png",
"cid": "logo-black.png"
}
]

View File

@ -0,0 +1,8 @@
<header>
<div class="logo">
<a href="https://www.verdnatura.es" target="_blank">
<img v-bind:src="getEmailSrc('logo-black.png')" alt="VerdNatura"/>
</a>
</div>
<div class="topbar"></div>
</header>

View File

@ -0,0 +1,4 @@
module.exports = {
name: 'email-header',
props: ['locale']
};

View File

@ -1,6 +1,6 @@
const CssReader = require(`${appPath}/lib/cssReader`);
const Stylesheet = require(`${appPath}/core/stylesheet`);
module.exports = new CssReader([
module.exports = new Stylesheet([
`${appPath}/common/css/layout.css`,
`${appPath}/common/css/report.css`,
`${appPath}/common/css/misc.css`,

View File

@ -0,0 +1,10 @@
numPages: Página {{page}} de {{pages}}
law:
phytosanitary: 'VERDNATURA LEVANTE SL - Pasaporte Fitosanitario R.P. Generalitat
Valenciana - Nº Comerciante: ES17462130'
privacy: En cumplimiento de lo dispuesto en la Ley Orgánica 15/1999, de Protección
de Datos de Carácter Personal, le comunicamos que los datos personales que facilite
se incluirán en ficheros automatizados de VERDNATURA LEVANTE S.L., pudiendo en
todo momento ejercitar los derechos de acceso, rectificación, cancelación y oposición,
comunicándolo por escrito al domicilio social de la entidad. La finalidad del
fichero es la gestión administrativa, contabilidad, y facturación.

View File

@ -0,0 +1,10 @@
numPages: Page {{page}} de {{pages}}
law:
phytosanitary: 'VERDNATURA LEVANTE SL - Passeport Phytosanitaire R.P. Generalitat
Valenciana - Numéro d''opérateur: ES17462130'
privacy: Conformément aux dispositions de la loi organique 15/1999 sur la protection
des données personnelles, nous vous informons que les données personnelles que
vous fournissez seront incluses dans des dossiers. VERDNATURA LEVANTE S.L., vous
pouvez à tout moment, exercer les droits d'accès, de rectification, d'annulation
et d'opposition, en communiquant par écrit au siège social de la société. Le dossier
a pour objet la gestion administrative, la comptabilité et la facturation.

View File

@ -0,0 +1,10 @@
numPages: Página {{page}} de {{pages}}
law:
phytosanitary: 'VERDNATURA LEVANTE S.L - Passaporte Fitossanitário R.P. Generalitat
Valenciana - Nº Comerciante: ES17462130'
privacy: Em cumprimento do disposto na lei Orgânica 15/1999, de Protecção de Dados
de Carácter Pessoal, comunicamos que os dados pessoais que facilite se incluirão
nos ficheiros automatizados de VERDNATURA LEVANTE S.L., podendo em todo momento
exercer os direitos de acesso, rectificação, cancelação e oposição, comunicando
por escrito ao domicílio social da entidade. A finalidade do ficheiro é a gestão
administrativa, contabilidade e facturação.

View File

@ -1,7 +1,7 @@
<footer>
<section class="page">
<section :if="leftText">{{leftText}}</section>
<section :if="centerText" class="uppercase">{{centerText}}</section>
<section v-if="leftText">{{leftText}}</section>
<section v-if="centerText" class="uppercase">{{centerText}}</section>
<section class="number">{{$t('numPages')}}</section>
</section>
<p class="phytosanitary">{{$t('law.phytosanitary')}}</p>

View File

@ -1,8 +1,4 @@
module.exports = {
name: 'report-footer',
created() {
if (this.locale)
this.$i18n.locale = this.locale;
},
props: ['leftText', 'centerText', 'locale']
};

View File

@ -1,6 +1,6 @@
const CssReader = require(`${appPath}/lib/cssReader`);
const Stylesheet = require(`${appPath}/core/stylesheet`);
module.exports = new CssReader([
module.exports = new Stylesheet([
`${appPath}/common/css/layout.css`,
`${appPath}/common/css/report.css`,
`${appPath}/common/css/misc.css`,

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" x="0px" y="0px" width="226.229px" height="31.038px" viewBox="0 0 226.229 31.038" enable-background="new 0 0 226.229 31.038" xml:space="preserve" id="svg2" inkscape:version="0.48.1 r9760" sodipodi:docname="logo.svg"><metadata id="metadata61"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata><defs id="defs59"/><sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="1366" inkscape:window-height="710" id="namedview57" showgrid="false" inkscape:zoom="4.0755163" inkscape:cx="138.56745" inkscape:cy="16.509992" inkscape:window-x="0" inkscape:window-y="26" inkscape:window-maximized="1" inkscape:current-layer="svg2"/>
<g id="Background">
</g>
<g id="Guides">
</g>
<g id="Foreground">
<g id="g7">
<g id="g9">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.417,30.321L0,0h8.233l4.26,15.582l0.349,1.276 c0.521,1.866,0.918,3.431,1.191,4.693c0.15-0.618,0.335-1.345,0.555-2.182c0.219-0.837,0.528-1.935,0.925-3.293L19.981,0h8.19 L17.671,30.321H10.417z" id="path11"/>
</g>
<g id="g13">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#A0CE67" d="M139.809,19.787c-0.665,0.357-1.748,0.686-3.25,0.988 c-0.727,0.137-1.283,0.254-1.667,0.35c-0.95,0.247-1.661,0.563-2.134,0.947c-0.472,0.384-0.799,0.899-0.979,1.544 c-0.223,0.796-0.155,1.438,0.204,1.925c0.359,0.488,0.945,0.731,1.757,0.731c1.252,0,2.375-0.36,3.369-1.081 c0.994-0.721,1.653-1.665,1.98-2.831L139.809,19.787z M144.915,30.321h-7.458c0.017-0.356,0.048-0.726,0.094-1.11l0.159-1.192 c-1.318,1.026-2.627,1.786-3.927,2.279c-1.299,0.493-2.643,0.739-4.031,0.739c-2.158,0-3.7-0.593-4.625-1.779 c-0.925-1.187-1.106-2.788-0.542-4.804c0.519-1.851,1.431-3.356,2.737-4.515c1.307-1.159,3.021-1.972,5.142-2.438 c1.169-0.247,2.641-0.515,4.413-0.803c2.646-0.412,4.082-1.016,4.304-1.812l0.151-0.539c0.182-0.65,0.076-1.145-0.317-1.483 c-0.393-0.339-1.071-0.508-2.033-0.508c-1.045,0-1.934,0.214-2.666,0.643c-0.731,0.428-1.289,1.058-1.673,1.887h-6.748 c1.065-2.53,2.64-4.413,4.723-5.65s4.724-1.856,7.923-1.856c1.991,0,3.602,0.241,4.833,0.722s2.095,1.209,2.59,2.185 c0.339,0.701,0.483,1.536,0.432,2.504c-0.052,0.969-0.377,2.525-0.978,4.669l-2.375,8.483c-0.284,1.014-0.416,1.812-0.396,2.395 s0.188,0.962,0.503,1.141L144.915,30.321z" id="path15" style="fill:#8ed300;fill-opacity:1"/>
</g>
<g id="g17">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#A0CE67" d="M185.7,30.321l6.27-22.393h7.049l-1.097,3.918 c1.213-1.537,2.502-2.659,3.867-3.366c1.365-0.707,2.951-1.074,4.758-1.101l-2.03,7.25c-0.304-0.042-0.608-0.072-0.912-0.093 c-0.303-0.02-0.592-0.03-0.867-0.03c-1.126,0-2.104,0.168-2.932,0.504c-0.829,0.336-1.561,0.854-2.197,1.555 c-0.406,0.467-0.789,1.136-1.149,2.007c-0.361,0.872-0.814,2.282-1.359,4.232l-2.104,7.516H185.7z" id="path19" style="fill:#8ed300;fill-opacity:1"/>
</g>
<g id="g21">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#A0CE67" d="M217.631,19.787c-0.664,0.357-1.748,0.686-3.25,0.988 c-0.727,0.137-1.282,0.254-1.667,0.35c-0.95,0.247-1.661,0.563-2.134,0.947c-0.472,0.384-0.799,0.899-0.979,1.544 c-0.223,0.796-0.155,1.438,0.205,1.925c0.359,0.488,0.945,0.731,1.757,0.731c1.252,0,2.375-0.36,3.369-1.081 c0.994-0.721,1.654-1.665,1.98-2.831L217.631,19.787z M222.737,30.321h-7.458c0.017-0.356,0.048-0.726,0.094-1.11l0.159-1.192 c-1.318,1.026-2.627,1.786-3.927,2.279c-1.299,0.493-2.643,0.739-4.031,0.739c-2.158,0-3.7-0.593-4.625-1.779 c-0.926-1.187-1.106-2.788-0.542-4.804c0.519-1.851,1.431-3.356,2.737-4.515c1.306-1.159,3.02-1.972,5.142-2.438 c1.169-0.247,2.641-0.515,4.413-0.803c2.647-0.412,4.082-1.016,4.304-1.812l0.151-0.539c0.182-0.65,0.077-1.145-0.317-1.483 c-0.393-0.339-1.071-0.508-2.033-0.508c-1.045,0-1.934,0.214-2.666,0.643c-0.731,0.428-1.289,1.058-1.672,1.887h-6.748 c1.065-2.53,2.64-4.413,4.723-5.65s4.724-1.856,7.923-1.856c1.99,0,3.601,0.241,4.833,0.722s2.095,1.209,2.591,2.185 c0.339,0.701,0.483,1.536,0.431,2.504c-0.051,0.969-0.377,2.525-0.978,4.669l-2.375,8.483c-0.284,1.014-0.416,1.812-0.396,2.395 c0.02,0.583,0.188,0.962,0.503,1.141L222.737,30.321z" id="path23" style="fill:#8ed300;fill-opacity:1"/>
</g>
<g id="g25">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#A0CE67" d="M188.386,7.928l-6.269,22.393h-7.174l0.864-3.085 c-1.227,1.246-2.476,2.163-3.746,2.751s-2.625,0.882-4.067,0.882c-2.471,0-4.154-0.634-5.048-1.901 c-0.895-1.268-0.993-3.149-0.294-5.644l4.31-15.396h7.338l-3.508,12.53c-0.516,1.842-0.641,3.109-0.375,3.803 s0.967,1.041,2.105,1.041c1.275,0,2.323-0.422,3.142-1.267c0.819-0.845,1.497-2.223,2.031-4.133l3.353-11.974H188.386z" id="path27" style="fill:#8ed300;fill-opacity:1"/>
</g>
<g id="g29">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#A0CE67" d="M149.937,12.356l1.239-4.428h2.995l1.771-6.326h7.338 l-1.771,6.326h3.753l-1.24,4.428h-3.753l-2.716,9.702c-0.416,1.483-0.498,2.465-0.247,2.946c0.25,0.48,0.905,0.721,1.964,0.721 l0.549-0.011l0.39-0.031l-1.31,4.678c-0.811,0.148-1.596,0.263-2.354,0.344c-0.758,0.081-1.48,0.122-2.167,0.122 c-2.543,0-4.108-0.621-4.695-1.863c-0.587-1.242-0.313-3.887,0.82-7.936l2.428-8.672H149.937z" id="path31" style="fill:#8ed300;fill-opacity:1"/>
</g>
<g id="g33">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M73.875,18.896c-0.561,2.004-0.616,3.537-0.167,4.601 s1.375,1.595,2.774,1.595c1.399,0,2.605-0.524,3.62-1.574s1.806-2.59,2.375-4.622c0.526-1.879,0.556-3.334,0.09-4.363 c-0.466-1.029-1.393-1.543-2.778-1.543c-1.304,0-2.487,0.528-3.551,1.585S74.386,17.071,73.875,18.896z M96.513,0l-8.489,30.321 h-7.337l0.824-2.944c-1.166,1.22-2.369,2.121-3.61,2.703s-2.583,0.874-4.025,0.874c-2.802,0-4.772-1.081-5.912-3.243 c-1.139-2.162-1.218-4.993-0.238-8.493c0.988-3.528,2.668-6.404,5.042-8.627c2.374-2.224,4.927-3.336,7.661-3.336 c1.47,0,2.695,0.296,3.676,0.887c0.981,0.591,1.681,1.465,2.099,2.62L89.217,0H96.513z" id="path35"/>
<g id="g37">
<path fill-rule="evenodd" clip-rule="evenodd" d="M73.875,18.896c-0.561,2.004-0.616,3.537-0.167,4.601s1.375,1.595,2.774,1.595 c1.399,0,2.605-0.524,3.62-1.574s1.806-2.59,2.375-4.622c0.526-1.879,0.556-3.334,0.09-4.363 c-0.466-1.029-1.393-1.543-2.778-1.543c-1.304,0-2.487,0.528-3.551,1.585S74.386,17.071,73.875,18.896z M96.513,0l-8.489,30.321 h-7.337l0.824-2.944c-1.166,1.22-2.369,2.121-3.61,2.703s-2.583,0.874-4.025,0.874c-2.802,0-4.772-1.081-5.912-3.243 c-1.139-2.162-1.218-4.993-0.238-8.493c0.988-3.528,2.668-6.404,5.042-8.627c2.374-2.224,4.927-3.336,7.661-3.336 c1.47,0,2.695,0.296,3.676,0.887c0.981,0.591,1.681,1.465,2.099,2.62L89.217,0H96.513z" id="path39"/>
</g>
</g>
<g id="g41">
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.488,30.321l6.269-22.393h7.049l-1.098,3.918 c1.213-1.537,2.502-2.659,3.868-3.366s6.015-1.074,7.822-1.101l-2.03,7.25c-0.304-0.042-0.608-0.072-0.911-0.093 c-0.304-0.02-0.592-0.03-0.867-0.03c-1.126,0-5.167,0.168-5.997,0.504c-0.829,0.336-1.561,0.854-2.196,1.555 c-0.406,0.467-0.789,1.136-1.149,2.007c-0.361,0.872-0.814,2.282-1.36,4.232l-2.104,7.516H46.488z" id="path43"/>
</g>
<g id="g45">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M32.673,16.742l8.351-0.021 c0.375-1.436,0.308-2.558-0.201-3.365s-1.402-1.211-2.68-1.211c-1.209,0-2.285,0.397-3.229,1.19S33.224,15.265,32.673,16.742z M38.817,23.278h7.043c-1.347,2.456-3.172,4.356-5.477,5.7c-2.305,1.345-4.885,2.017-7.74,2.017 c-3.473,0-5.923-1.054-7.351-3.161c-1.427-2.107-1.632-4.98-0.613-8.618c1.038-3.707,2.875-6.641,5.512-8.803 c2.637-2.163,5.678-3.244,9.123-3.244c3.555,0,6.04,1.099,7.456,3.298c1.417,2.198,1.582,5.234,0.498,9.109l-0.239,0.814 l-0.167,0.484H31.721c-0.441,1.575-0.438,2.777,0.01,3.606c0.448,0.829,1.332,1.244,2.65,1.244c0.975,0,1.836-0.206,2.583-0.617 S38.33,24.086,38.817,23.278z" id="path47"/>
<g id="g49">
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.673,16.742l8.351-0.021c0.375-1.436,0.308-2.558-0.201-3.365 s-1.402-1.211-2.68-1.211c-1.209,0-2.285,0.397-3.229,1.19S33.224,15.265,32.673,16.742z M38.817,23.278h7.043 c-1.347,2.456-3.172,4.356-5.477,5.7c-2.305,1.345-4.885,2.017-7.74,2.017c-3.473,0-5.923-1.054-7.351-3.161 c-1.427-2.107-1.632-4.98-0.613-8.618c1.038-3.707,2.875-6.641,5.512-8.803c2.637-2.163,5.678-3.244,9.123-3.244 c3.555,0,6.04,1.099,7.456,3.298c1.417,2.198,1.582,5.234,0.498,9.109l-0.239,0.814l-0.167,0.484H31.721 c-0.441,1.575-0.438,2.777,0.01,3.606c0.448,0.829,1.332,1.244,2.65,1.244c0.975,0,1.836-0.206,2.583-0.617 S38.33,24.086,38.817,23.278z" id="path51"/>
</g>
</g>
<g id="g53">
<path fill="#A0CE67" d="M112.881,30.643l-6.404-18.639l-6.455,18.639h-7.254l9.565-30.321h8.19l4.434,15.582l0.35,1.276 c0.521,1.866,0.917,3.431,1.191,4.693l0.555-2.182c0.219-0.837,0.528-1.935,0.925-3.293l4.468-16.076h8.19l-10.501,30.321 H112.881z" id="path55" style="fill:#8ed300;fill-opacity:1"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -0,0 +1,5 @@
company:
fiscalAddress: VERDNATURA LEVANTE S.L., B97367486 Avda. Espioca, 100, 46460 Silla
- www.verdnatura.es - clientes@verdnatura.es
registry: 'CIF: B97367486 Registro Mercantil de Valencia, Tomo 8041, Libro 5334,
Folio 160, Sección 8, Hoja V 102076'

View File

@ -0,0 +1,5 @@
company:
fiscalAddress: VERDNATURA LEVANTE S.L., B97367486 Avda. Espioca, 100, 46460 Silla
- www.verdnatura.es - clientes@verdnatura.es
registry: 'CIF: B97367486 Registro Mercantil de Valencia, Tomo 8041, Libro 5334,
Folio 160, Sección 8, Hoja V 102076'

View File

@ -1,5 +1,5 @@
<header>
<img :src="embeded['/assets/images/report-logo.svg']" alt="Verdnatura"/>
<img v-bind:src="getReportSrc('report-logo.svg')" alt="Verdnatura"/>
<section>{{$t('company.fiscalAddress')}}</section>
<section>{{$t('company.registry')}}</section>
</header>

View File

@ -0,0 +1,4 @@
module.exports = {
name: 'report-header',
props: ['isPreview', 'locale']
};

View File

@ -14,19 +14,6 @@ for (let configFile of configFiles) {
Object.assign(config, require(configFile));
}
/* let proxyConf = {};
let proxyFiles = [
'../../nginx/config.yml',
`${configPath}/config.yml`,
`${configPath}/config.${env}.yml`
];
for (let proxyFile of proxyFiles) {
if (fs.existsSync(proxyFile))
Object.assign(proxyConf, require(proxyFile));
} */
// config.proxy = proxyConf;
config.env = env;
module.exports = config;

24
print/core/database.js Normal file
View File

@ -0,0 +1,24 @@
const mysql = require('mysql2/promise');
const config = require('./config.js');
module.exports = {
init() {
if (!this.pool) {
this.pool = mysql.createPool(config.mysql);
this.pool.on('connection', connection => {
connection.config.namedPlaceholders = true;
});
}
},
find(query, params) {
return this.pool.query(query, params).then(([rows]) => {
return rows;
});
},
findOne(query, params) {
return this.find(query, params).then(([rows]) => rows);
},
findFromDef() {
}
};

View File

@ -0,0 +1,2 @@
// Import global directives
require('./pin');

View File

@ -0,0 +1,9 @@
// DIRECTIVES NOT WORKING
const Vue = require('vue');
Vue.directive('pin', {
bind: function(el, binding, vnode) {
el.style.position = 'fixed';
el.style.top = binding.value + 'px';
el.style.backgroundColor = 'red';
}
});

93
print/core/email.js Normal file
View File

@ -0,0 +1,93 @@
const path = require('path');
const smtp = require('./smtp');
const Component = require('./component');
const Report = require('./report');
const db = require('./database');
const config = require('./config');
if (!process.env.OPENSSL_CONF)
process.env.OPENSSL_CONF = '/etc/ssl/';
class Email extends Component {
constructor(name, args) {
super(name);
this.args = args;
}
get path() {
return `../templates/email/${this.name}`;
}
async getSubject() {
if (!this.lang) await this.getLang();
const locale = this.locale.messages;
const userLocale = locale[this.lang];
if (!userLocale) {
const fallbackLocale = config.i18n.fallbackLocale;
return locale[fallbackLocale].subject;
}
return userLocale.subject;
}
async getLang() {
const clientId = this.args.clientId;
const lang = await db.findOne(`
SELECT lang FROM account.user
WHERE id = ?`, [clientId]).then(rows => {
return rows.lang;
});
this.lang = lang;
}
async send() {
const instance = this.build();
const rendered = await this.render();
const attachments = [];
const getAttachments = async(componentPath, files) => {
for (file of files) {
const fileCopy = Object.assign({}, file);
if (fileCopy.cid) {
const templatePath = `${componentPath}/${file.path}`;
const fullFilePath = path.resolve(__dirname, templatePath);
fileCopy.path = path.resolve(__dirname, fullFilePath);
} else {
const reportName = fileCopy.filename.replace('.pdf', '');
const report = new Report(reportName, this.args);
fileCopy.content = await report.toPdfStream();
}
attachments.push(fileCopy);
}
};
if (instance.components) {
const components = instance.components;
for (let componentName in components) {
const component = components[componentName];
const componentPath = `./components/${componentName}`;
await getAttachments(componentPath, component.attachments);
}
}
if (this.attachments)
await getAttachments(this.path, this.attachments);
const localeSubject = await this.getSubject();
const options = {
to: this.args.recipient,
subject: localeSubject,
html: rendered,
attachments: attachments
};
return smtp.send(options);
}
}
module.exports = Email;

View File

@ -0,0 +1,9 @@
const Vue = require('vue');
const config = require('../config');
const defaultLocale = config.i18n.locale;
Vue.filter('currency', function(value, currency = 'EUR', locale = defaultLocale) {
return new Intl.NumberFormat(locale, {
style: 'currency', currency
}).format(parseFloat(value));
});

View File

@ -0,0 +1,6 @@
const Vue = require('vue');
const strftime = require('strftime');
Vue.filter('date', function(value, specifiers) {
return strftime(specifiers, value);
});

View File

@ -0,0 +1,4 @@
// Import global filters
require('./date');
require('./currency');
require('./percentage');

View File

@ -0,0 +1,11 @@
const Vue = require('vue');
const config = require('../config');
const defaultLocale = config.i18n.locale;
Vue.filter('percentage', function(value, minFraction = 2, maxFraction = 2, locale = defaultLocale) {
return new Intl.NumberFormat(locale, {
style: 'percent',
minimumFractionDigits: minFraction,
maximumFractionDigits: maxFraction
}).format(parseFloat(value));
});

View File

@ -0,0 +1,23 @@
const Vue = require('vue');
const config = require('../config');
const imageSrc = {
methods: {
getEmailSrc(image) {
let src = `cid:${image}`;
if (this.isPreview === 'true')
src = `/api/${this.$options.name}/assets/images/${image}`;
return src;
},
getReportSrc(image) {
const assetsPath = `${config.app.host}/api/${this.$options.name}`;
const imagePath = `${assetsPath}/assets/images/${image}`;
return imagePath;
}
}
};
Vue.mixin(imageSrc);

View File

@ -0,0 +1,4 @@
// Import global mixins
require('./image-src');
require('./user-locale');
require('./prop-validator');

View File

@ -0,0 +1,26 @@
const Vue = require('vue');
const validator = {
created() {
const props = this.$options.props;
const invalidProps = [];
for (prop in props) {
const isObject = typeof props[prop] === 'object';
const isRequired = props[prop].required;
const isNotDefined = this[prop] === undefined;
if (isObject && isRequired && isNotDefined)
invalidProps.push(prop);
}
if (invalidProps.length > 0) {
const required = invalidProps.join(', ');
throw new Error(`Required params not found [${required}]`);
}
},
props: ['isPreview']
};
Vue.mixin(validator);

View File

@ -0,0 +1,24 @@
const Vue = require('vue');
const db = require('../database');
const userLocale = {
async serverPrefetch() {
if (this.clientId)
this.locale = await this.getLocale(this.clientId);
if (this.locale)
this.$i18n.locale = this.locale;
},
methods: {
getLocale(clientId) {
return db.findOne(`
SELECT lang FROM account.user
WHERE id = ?`, [clientId]).then(rows => {
return rows.lang;
});
}
},
props: ['clientId']
};
Vue.mixin(userLocale);

38
print/core/report.js Normal file
View File

@ -0,0 +1,38 @@
const fs = require('fs');
const pdf = require('html-pdf');
const path = require('path');
const config = require('./config');
const Component = require('./component');
if (!process.env.OPENSSL_CONF)
process.env.OPENSSL_CONF = '/etc/ssl/';
class Report extends Component {
constructor(name, args) {
super(name);
this.args = args;
}
get path() {
return `../templates/reports/${this.name}`;
}
async toPdfStream() {
const template = await this.render();
let options = config.pdf;
const optionsPath = `${this.path}/options.json`;
const fullPath = path.resolve(__dirname, optionsPath);
if (fs.existsSync(fullPath))
options = Object.assign(options, require(optionsPath));
return new Promise(resolve => {
pdf.create(template, options).toStream((err, stream) => {
resolve(stream);
});
});
}
}
module.exports = Report;

51
print/core/router.js Normal file
View File

@ -0,0 +1,51 @@
const Report = require('./report');
const Email = require('./email');
module.exports = app => {
app.get(`/api/report/:name`, async(req, res, next) => {
const args = req.query;
const requiredArgs = ['clientId'];
const hasRequiredArgs = requiredArgs.every(arg => {
return args[arg];
});
if (!hasRequiredArgs)
res.json({message: 'Required params recipient, clientId'});
try {
const report = new Report(req.params.name, args);
const stream = await report.toPdfStream();
res.setHeader('Content-type', 'application/pdf');
stream.pipe(res);
} catch (e) {
next(e);
}
});
app.get(`/api/email/:name`, async(req, res, next) => {
const args = req.query;
const requiredArgs = ['recipient', 'clientId'];
const hasRequiredArgs = requiredArgs.every(arg => {
return args[arg];
});
if (!hasRequiredArgs)
res.json({message: 'Required params recipient, clientId'});
try {
const email = new Email(req.params.name, args);
if (args.isPreview === 'true') {
const rendered = await email.render();
res.send(rendered);
} else {
await email.send();
res.status(200).json({message: 'Sent'});
}
} catch (e) {
next(e);
}
});
};

View File

@ -1,5 +1,5 @@
const nodemailer = require('nodemailer');
const config = require('./config.js');
const config = require('./config');
module.exports = {
init() {
@ -11,7 +11,7 @@ module.exports = {
options.from = `${config.app.senderName} <${config.app.senderMail}>`;
if (process.env.NODE_ENV !== 'production') {
if (!config.smtp.user)
if (!config.smtp.auth.user)
return Promise.resolve(true);
options.to = config.app.senderMail;

View File

@ -1,6 +1,6 @@
const fs = require('fs-extra');
class CssReader {
class Stylesheet {
constructor(files) {
this.files = files;
this.css = [];
@ -15,4 +15,4 @@ class CssReader {
}
}
module.exports = CssReader;
module.exports = Stylesheet;

View File

@ -1,9 +0,0 @@
const mysql = require('mysql2/promise');
const config = require('./config.js');
module.exports = {
init() {
if (!this.pool)
this.pool = mysql.createPool(config.mysql);
},
};

View File

@ -1,152 +0,0 @@
const Vue = require('vue');
const VueI18n = require('vue-i18n');
const renderer = require('vue-server-renderer').createRenderer();
const fs = require('fs-extra');
const juice = require('juice');
const smtp = require('./smtp');
const fallbackLocale = 'es';
if (!process.env.OPENSSL_CONF)
process.env.OPENSSL_CONF = '/etc/ssl/';
Vue.use(VueI18n);
module.exports = {
path: `${appPath}/report`,
/**
* Renders a report component
*
* @param {String} name - Report name
* @param {Object} ctx - Request context
*/
async render(name, ctx) {
const component = require(`${this.path}/${name}`);
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: result.mergedData,
};
});
},
/**
* Prefetch all component data from asyncData method
*
* @param {Object} orgComponent - Component object
* @param {Object} ctx - Request context
*/
async preFetch(orgComponent, ctx) {
let component = Object.create(orgComponent);
let mergedData = {attachments: []};
let asyncData = {};
let data = {};
let params = {};
if (Object.keys(ctx.body).length > 0)
params = ctx.body;
if (Object.keys(ctx.query).length > 0)
params = ctx.query;
await this.attachAssets(component);
if (orgComponent.hasOwnProperty('data'))
data = orgComponent.data();
if (orgComponent.hasOwnProperty('asyncData')) {
asyncData = await orgComponent.asyncData(ctx, params);
if (asyncData.locale) {
let locale = component.i18n.messages[asyncData.locale];
if (!locale)
locale = component.i18n.messages[fallbackLocale];
mergedData.subject = locale.subject;
}
}
mergedData = Object.assign(mergedData, data, asyncData);
component.data = function data() {
return mergedData;
};
if (data.hasOwnProperty('files')) {
const files = data.files;
files.forEach(file => {
const componentPath = `${this.path}/${orgComponent.name}`;
let fileSrc = componentPath + file;
if (file.slice(0, 4) === 'http' || file.slice(0, 5) === 'https')
fileSrc = file;
const fileName = file.split('/').pop();
mergedData.attachments.push({
filename: fileName,
path: fileSrc,
cid: file,
});
});
}
const components = orgComponent.components;
if (components) {
const promises = [];
const childNames = [];
component.components = {};
Object.keys(components).forEach(childName => {
childNames.push(childName);
promises.push(this.preFetch(components[childName], ctx));
});
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 {component, mergedData};
},
async attachAssets(component) {
const localePath = `${this.path}/${component.name}/locale.js`;
const templatePath = `${this.path}/${component.name}/index.html`;
const stylePath = `${this.path}/${component.name}/assets/css/index.js`;
const template = await fs.readFile(templatePath, 'utf8');
const css = require(stylePath);
component.i18n = require(localePath);
component.template = juice.inlineContent(template, css);
},
async toEmail(name, ctx) {
const rendered = await this.render(name, ctx);
const data = rendered.data;
const options = {
to: data.recipient,
subject: data.subject,
html: rendered.html,
attachments: data.attachments,
};
return smtp.send(options);
},
};

View File

@ -1,12 +0,0 @@
module.exports = class UserException extends Error {
/**
* UserException
* @param {String} message - Error message
*/
constructor(message) {
super(message);
this.httpStatusCode = 400;
this.name = 'UserException';
}
};

View File

@ -1,7 +0,0 @@
module.exports = {
methods: {
uFirst: (text) => {
return text;
},
},
};

View File

@ -1,119 +0,0 @@
const Vue = require('vue');
const VueI18n = require('vue-i18n');
const renderer = require('vue-server-renderer').createRenderer();
const fs = require('fs-extra');
const pdf = require('html-pdf');
const juice = require('juice');
const config = require('./config');
Vue.use(VueI18n);
if (!process.env.OPENSSL_CONF)
process.env.OPENSSL_CONF = '/etc/ssl/';
module.exports = {
path: `${appPath}/report`,
/**
* Renders a report component
*
* @param {String} name - Report name
* @param {Object} ctx - Request context
*/
async render(name, ctx) {
const component = require(`${this.path}/${name}`);
const result = await this.preFetch(component, ctx);
const i18n = new VueI18n({
locale: 'es',
fallbackLocale: 'es',
silentTranslationWarn: true
});
const app = new Vue({i18n,
render: h => h(result.component)});
return renderer.renderToString(app);
},
/**
* Prefetch all component data from asyncData method
*
* @param {Object} orgComponent - Component object
* @param {Object} ctx - Request context
*/
async preFetch(orgComponent, ctx) {
let component = Object.create(orgComponent);
let mergedData = {};
let asyncData = {};
let data = {};
let params = {};
if (Object.keys(ctx.body).length > 0)
params = ctx.body;
if (Object.keys(ctx.query).length > 0)
params = ctx.query;
await this.attachAssets(component);
if (orgComponent.hasOwnProperty('data'))
data = orgComponent.data();
if (orgComponent.hasOwnProperty('asyncData'))
asyncData = await orgComponent.asyncData(ctx, params);
mergedData = Object.assign(mergedData, data, asyncData);
component.data = function data() {
return mergedData;
};
const components = orgComponent.components;
if (components) {
const promises = [];
const childNames = [];
component.components = {};
Object.keys(components).forEach(childName => {
childNames.push(childName);
promises.push(this.preFetch(components[childName], ctx));
});
await Promise.all(promises).then(results => {
results.forEach((result, i) => {
component.components[childNames[i]] = result.component;
});
});
}
return {component};
},
async attachAssets(component) {
const localePath = `${this.path}/${component.name}/locale`;
const templatePath = `${this.path}/${component.name}/index.html`;
const stylePath = `${this.path}/${component.name}/assets/css/index`;
const template = await fs.readFile(templatePath, 'utf8');
const css = require(stylePath);
const cssOptions = {inlinePseudoElements: true};
component.i18n = require(localePath);
component.template = juice.inlineContent(template, css, cssOptions);
},
async toPdf(name, ctx) {
const html = await this.render(name, ctx);
let options = config.pdf;
const optionsPath = `${this.path}/${name}/options.json`;
if (fs.existsSync(optionsPath))
options = Object.assign(options, require(optionsPath));
return new Promise(resolve => {
pdf.create(html, options).toStream((err, stream) => {
resolve(stream);
});
});
},
};

View File

@ -1,64 +0,0 @@
const reportEngine = require('./reportEngine.js');
const emailEngine = require('./emailEngine');
const express = require('express');
const routes = require(`../config/routes.json`);
module.exports = app => {
this.path = `${appPath}/report`;
/**
* Enables a report
*
* @param {String} name - Report state path
*/
function registerReport(name) {
if (!name) throw new Error('Report name required');
app.get(`/api/report/${name}`, (request, response, next) => {
reportEngine.toPdf(name, request).then(stream => {
response.setHeader('Content-type', 'application/pdf');
stream.pipe(response);
}).catch(e => {
next(e);
});
});
}
/**
* Enables a email
*
* @param {String} name - Report state path
*/
function registerEmail(name) {
if (!name) throw new Error('Email name required');
app.get(`/api/email/${name}`, (request, response, next) => {
emailEngine.render(name, request).then(rendered => {
response.send(rendered.html);
}).catch(e => {
next(e);
});
});
app.post(`/api/email/${name}`, (request, response, next) => {
emailEngine.toEmail(name, request).then(() => {
response.status(200).json({status: 200});
}).catch(e => {
next(e);
});
});
}
/**
* Register routes
*/
routes.forEach(route => {
if (route.type === 'email')
registerEmail(route.name);
else if (route.type === 'report')
registerReport(route.name);
const staticPath = this.path + `/${route.name}/assets`;
app.use(`/api/assets`, express.static(staticPath));
});
};

583
print/package-lock.json generated
View File

@ -21,9 +21,20 @@
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": {
"sprintf-js": "~1.0.2"
}
},
"asn1": {
"version": "0.2.4",
@ -39,12 +50,9 @@
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
"async": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
"integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
"requires": {
"lodash": "^4.17.10"
}
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/async/-/async-3.1.0.tgz",
"integrity": "sha512-4vx/aaY6j/j3Lw3fbCHNWP0pPaTCew3F6F3hYyl/tHs/ndmV1q7NW9T5yuJ2XAGwdQrP+6Wu20x06U4APo/iQQ=="
},
"asynckit": {
"version": "0.4.0",
@ -80,21 +88,24 @@
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"optional": true
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"cheerio": {
@ -120,6 +131,44 @@
"lodash.some": "^4.4.0"
}
},
"cliui": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
"requires": {
"string-width": "^3.1.0",
"strip-ansi": "^5.2.0",
"wrap-ansi": "^5.1.0"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"requires": {
"ansi-regex": "^4.1.0"
}
}
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"combined-stream": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
@ -129,9 +178,9 @@
}
},
"commander": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
},
"concat-stream": {
"version": "1.6.2",
@ -200,9 +249,9 @@
}
},
"css-what": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.2.tgz",
"integrity": "sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ=="
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
"integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg=="
},
"dashdash": {
"version": "1.14.1",
@ -213,13 +262,12 @@
}
},
"datauri": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/datauri/-/datauri-1.1.0.tgz",
"integrity": "sha512-0q+cTTKx7q8eDteZRIQLTFJuiIsVing17UbWTPssY4JLSMaYsk/VKpNulBDo9NSgQWcvlPrkEHW8kUO67T/7mQ==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/datauri/-/datauri-2.0.0.tgz",
"integrity": "sha512-zS2HSf9pI5XPlNZgIqJg/wCJpecgU/HA6E/uv2EfaWnW1EiTGLfy/EexTIsC9c99yoCOTXlqeeWk4FkCSuO3/g==",
"requires": {
"image-size": "^0.6.2",
"mimer": "^0.3.2",
"semver": "^5.5.0"
"image-size": "^0.7.3",
"mimer": "^1.0.0"
}
},
"debug": {
@ -231,6 +279,11 @@
"ms": "2.0.0"
}
},
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
"deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
@ -242,24 +295,22 @@
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"denque": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-1.4.0.tgz",
"integrity": "sha512-gh513ac7aiKrAgjiIBWZG0EASyDF9p4JMWwKA8YU5s9figrL5SRNEMT6FDynsegakuhWd1wVqTvqvqAoDxw7wQ=="
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
"integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
},
"dijkstrajs": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.1.tgz",
"integrity": "sha1-082BIh4+pAdCz83lVtTpnpjdxxs="
},
"dom-serializer": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
"integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
"integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
"requires": {
"domelementtype": "~1.1.1",
"entities": "~1.1.1"
},
"dependencies": {
"domelementtype": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
"integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs="
}
"domelementtype": "^1.3.0",
"entities": "^1.1.1"
}
},
"domelementtype": {
@ -293,6 +344,11 @@
"safer-buffer": "^2.1.0"
}
},
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
},
"entities": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
@ -309,6 +365,11 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@ -350,6 +411,14 @@
"pend": "~1.2.0"
}
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"requires": {
"locate-path": "^3.0.0"
}
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@ -383,6 +452,11 @@
"is-property": "^1.0.2"
}
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
@ -418,6 +492,11 @@
"ansi-regex": "^2.0.0"
}
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"hash-sum": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
@ -447,16 +526,16 @@
}
},
"htmlparser2": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.0.tgz",
"integrity": "sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ==",
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
"integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
"requires": {
"domelementtype": "^1.3.0",
"domelementtype": "^1.3.1",
"domhandler": "^2.3.0",
"domutils": "^1.5.1",
"entities": "^1.1.1",
"inherits": "^2.0.1",
"readable-stream": "^3.0.6"
"readable-stream": "^3.1.1"
}
},
"http-signature": {
@ -470,23 +549,33 @@
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.0.tgz",
"integrity": "sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"image-size": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz",
"integrity": "sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA=="
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.7.5.tgz",
"integrity": "sha512-Hiyv+mXHfFEP7LzUL/llg9RwFxxY+o9N3JVLIeG5E7iFIFAalxvRU9UZthBdYDEVnzHMgjnKJPPpay5BWf1g9g=="
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"intl": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/intl/-/intl-1.2.5.tgz",
"integrity": "sha1-giRKIZDE5Bn4Nx9ao02qNCDiq94="
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"is-property": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
@ -519,6 +608,15 @@
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"js-yaml": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
@ -559,9 +657,9 @@
}
},
"juice": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/juice/-/juice-5.1.0.tgz",
"integrity": "sha512-SRfLv0y7xwAKnsd6HWS9aSF0+a9ozXEatKlXHiiTvozdZu0Vwz9dDk7NEpy/N2icFTnhYtk1Du3rPNtH7fFVHw==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/juice/-/juice-5.2.0.tgz",
"integrity": "sha512-0l6GZmT3efexyaaay3SchKT5kG311N59TEFP5lfvEy0nz9SNqjx311plJ3b4jze7arsmDsiHQLh/xnAuk0HFTQ==",
"requires": {
"cheerio": "^0.22.0",
"commander": "^2.15.1",
@ -569,7 +667,7 @@
"deep-extend": "^0.6.0",
"mensch": "^0.3.3",
"slick": "^1.12.2",
"web-resource-inliner": "^4.2.1"
"web-resource-inliner": "^4.3.1"
}
},
"kew": {
@ -587,10 +685,14 @@
"graceful-fs": "^4.1.9"
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"lodash._reinterpolate": {
"version": "3.0.0",
@ -633,9 +735,9 @@
"integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM="
},
"lodash.merge": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz",
"integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ=="
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
"lodash.pick": {
"version": "4.4.0",
@ -658,20 +760,20 @@
"integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0="
},
"lodash.template": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz",
"integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=",
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
"integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
"requires": {
"lodash._reinterpolate": "~3.0.0",
"lodash._reinterpolate": "^3.0.0",
"lodash.templatesettings": "^4.0.0"
}
},
"lodash.templatesettings": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz",
"integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz",
"integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==",
"requires": {
"lodash._reinterpolate": "~3.0.0"
"lodash._reinterpolate": "^3.0.0"
}
},
"lodash.unescape": {
@ -690,12 +792,11 @@
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
},
"lru-cache": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"requires": {
"pseudomap": "^1.0.2",
"yallist": "^2.1.2"
"yallist": "^3.0.2"
}
},
"mensch": {
@ -717,9 +818,9 @@
}
},
"mimer": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/mimer/-/mimer-0.3.2.tgz",
"integrity": "sha512-N6NcgDQAevhP/02DQ/epK6daLy4NKrIHyTlJcO6qBiYn98q+Y4a/knNsAATCe1xLS2F0nEmJp+QYli2s8vKwyQ=="
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mimer/-/mimer-1.0.0.tgz",
"integrity": "sha512-4ZJvCzfcwsBgPbkKXUzGoVZMWjv8IDIygkGzVc7uUYhgnK0t2LmGxxjdgH1i+pn0/KQfB5F/VKUJlfyTSOFQjg=="
},
"minimist": {
"version": "0.0.8",
@ -743,15 +844,15 @@
"optional": true
},
"mysql2": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-1.6.5.tgz",
"integrity": "sha512-zedaOOyb3msuuZcJJnxIX/EGOpmljDG7B+UevRH5lqcv+yhy9eCwkArBz8/AO+/rlY3/oCsOdG8R5oD6k0hNfg==",
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-1.7.0.tgz",
"integrity": "sha512-xTWWQPjP5rcrceZQ7CSTKR/4XIDeH/cRkNH/uzvVGQ7W5c7EJ0dXeJUusk7OKhIoHj7uFKUxDVSCfLIl+jluog==",
"requires": {
"denque": "^1.4.0",
"denque": "^1.4.1",
"generate-function": "^2.3.1",
"iconv-lite": "^0.4.24",
"iconv-lite": "^0.5.0",
"long": "^4.0.0",
"lru-cache": "^4.1.3",
"lru-cache": "^5.1.1",
"named-placeholders": "^1.1.2",
"seq-queue": "^0.0.5",
"sqlstring": "^2.3.1"
@ -763,6 +864,22 @@
"integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==",
"requires": {
"lru-cache": "^4.1.3"
},
"dependencies": {
"lru-cache": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
"requires": {
"pseudomap": "^1.0.2",
"yallist": "^2.1.2"
}
},
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
}
}
},
"nice-try": {
@ -788,6 +905,32 @@
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
},
"p-limit": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
"integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"requires": {
"p-limit": "^2.0.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
},
"path-key": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
@ -863,6 +1006,11 @@
"pinkie": "^2.0.0"
}
},
"pngjs": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
"integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="
},
"process-nextick-args": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
@ -890,15 +1038,33 @@
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"qrcode": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.2.tgz",
"integrity": "sha512-eR6RgxFYPDFH+zFLTJKtoNP/RlsHANQb52AUmQ2bGDPMuUw7jJb0F+DNEgx7qQGIElrbFxWYMc0/B91zLZPF9Q==",
"requires": {
"dijkstrajs": "^1.0.1",
"isarray": "^2.0.1",
"pngjs": "^3.3.0",
"yargs": "^13.2.4"
},
"dependencies": {
"isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
}
}
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"readable-stream": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz",
"integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==",
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
"integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@ -941,10 +1107,20 @@
"throttleit": "^1.0.0"
}
},
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"resolve": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
"integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
"integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
"requires": {
"path-parse": "^1.0.6"
}
@ -960,9 +1136,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"semver": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg=="
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
},
"seq-queue": {
"version": "0.0.5",
@ -970,9 +1146,14 @@
"integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4="
},
"serialize-javascript": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz",
"integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw=="
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
"integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A=="
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"shebang-command": {
"version": "1.2.0",
@ -997,6 +1178,11 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
"integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI="
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"sqlstring": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
@ -1023,12 +1209,44 @@
"resolved": "https://registry.npmjs.org/strftime/-/strftime-0.10.0.tgz",
"integrity": "sha1-s/D6QZKVICpaKJ9ta+n0kJphcZM="
},
"string_decoder": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
"integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==",
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"requires": {
"safe-buffer": "~5.1.0"
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"requires": {
"ansi-regex": "^4.1.0"
}
}
}
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
}
}
},
"strip-ansi": {
@ -1040,9 +1258,12 @@
}
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"requires": {
"has-flag": "^3.0.0"
}
},
"throttleit": {
"version": "1.0.0",
@ -1109,9 +1330,9 @@
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
},
"valid-data-url": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz",
"integrity": "sha512-FXg2qXMzfAhZc0y2HzELNfUeiOjPr+52hU1DNBWiJJ2luXD+dD1R9NA48Ug5aj0ibbxroeGDc/RJv6ThiGgkDw=="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-2.0.0.tgz",
"integrity": "sha512-dyCZnv3aCey7yfTgIqdZanKl7xWAEEKCbgmR7SKqyK6QT/Z07ROactrgD1eA37C69ODRj7rNOjzKWVPh0EUjBA=="
},
"verror": {
"version": "1.10.0",
@ -1124,19 +1345,19 @@
}
},
"vue": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.7.tgz",
"integrity": "sha512-g7ADfQ82QU+j6F/bVDioVQf2ccIMYLuR4E8ev+RsDBlmwRkhGO3HhgF4PF9vpwjdPpxyb1zzLur2nQ2oIMAMEg=="
"version": "2.6.10",
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.10.tgz",
"integrity": "sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ=="
},
"vue-i18n": {
"version": "8.8.2",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.8.2.tgz",
"integrity": "sha512-P09ZN2S0mX1AmhSR/+wP2owP3izGVx1pSoDFcOXTLya5xvP95dG7kc9LQUnboPgSzK/JKe9FkYmoYdDTKDjPSw=="
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.15.0.tgz",
"integrity": "sha512-juJ/avAP39bOMycC+qQDLJ8U9z9LtLF/9PsRoJLBSfsYZo9bqYntyyX5QPicwlb1emJKjgxhZ3YofHiQcXBu0Q=="
},
"vue-server-renderer": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/vue-server-renderer/-/vue-server-renderer-2.6.7.tgz",
"integrity": "sha512-CVtGR+bE63y4kyIeOcCEF2UNKquSquFQAsTHZ5R1cGM4L4Z0BXgAUEcngTOy8kN+tubt3c1zpRvbrok/bHKeDg==",
"version": "2.6.10",
"resolved": "https://registry.npmjs.org/vue-server-renderer/-/vue-server-renderer-2.6.10.tgz",
"integrity": "sha512-UYoCEutBpKzL2fKCwx8zlRtRtwxbPZXKTqbl2iIF4yRZUNO/ovrHyDAJDljft0kd+K0tZhN53XRHkgvCZoIhug==",
"requires": {
"chalk": "^1.1.3",
"hash-sum": "^1.0.2",
@ -1146,21 +1367,46 @@
"resolve": "^1.2.0",
"serialize-javascript": "^1.3.0",
"source-map": "0.5.6"
},
"dependencies": {
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
}
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
}
}
},
"web-resource-inliner": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-4.2.1.tgz",
"integrity": "sha512-fOWnBQHVX8zHvEbECDTxtYL0FXIIZZ5H3LWoez8mGopYJK7inEru1kVMDzM1lVdeJBNEqUnNP5FBGxvzuMcwwQ==",
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-4.3.3.tgz",
"integrity": "sha512-Qk19pohqZs3SoFUW4ZlOHlM8hsOnXhTpCrQ16b1qtJtKzJgO7NZLGP+/lcb2g3hWDQD39/LE/JYOn1Sjy7tn1A==",
"requires": {
"async": "^2.1.2",
"chalk": "^1.1.3",
"datauri": "^1.0.4",
"async": "^3.1.0",
"chalk": "^2.4.2",
"datauri": "^2.0.0",
"htmlparser2": "^3.9.2",
"lodash.unescape": "^4.0.1",
"request": "^2.78.0",
"valid-data-url": "^0.1.4",
"xtend": "^4.0.0"
"safer-buffer": "^2.1.2",
"valid-data-url": "^2.0.0",
"xtend": "^4.0.2"
}
},
"which": {
@ -1171,15 +1417,84 @@
"isexe": "^2.0.0"
}
},
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"wrap-ansi": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
"requires": {
"ansi-styles": "^3.2.0",
"string-width": "^3.0.0",
"strip-ansi": "^5.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"requires": {
"ansi-regex": "^4.1.0"
}
}
}
},
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
},
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
},
"yargs": {
"version": "13.3.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
"integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
"requires": {
"cliui": "^5.0.0",
"find-up": "^3.0.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^13.1.1"
}
},
"yargs-parser": {
"version": "13.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
"integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
},
"yauzl": {
"version": "2.4.1",

View File

@ -15,13 +15,15 @@
"dependencies": {
"fs-extra": "^7.0.1",
"html-pdf": "^2.2.0",
"juice": "^5.0.1",
"mysql2": "^1.6.5",
"intl": "^1.2.5",
"js-yaml": "^3.13.1",
"juice": "^5.2.0",
"mysql2": "^1.7.0",
"nodemailer": "^4.7.0",
"qrcode": "^1.4.2",
"strftime": "^0.10.0",
"vue": "^2.6.7",
"vue-i18n": "^8.8.2",
"vue-server-renderer": "^2.6.7"
"vue": "^2.6.10",
"vue-i18n": "^8.15.0",
"vue-server-renderer": "^2.6.10"
}
}

View File

@ -1,36 +0,0 @@
<!DOCTYPE html>
<html lang="es">
<head>
<title>{{ $t('subject') }}</title>
</head>
<body>
<section class="container">
<!-- Header component -->
<email-header></email-header>
<!-- End header component -->
<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>
<!-- Footer component -->
<email-footer :locale="locale"></email-footer>
<!-- End footer component -->
</section>
</body>
</html>

View File

@ -1,53 +0,0 @@
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() {
if (this.locale)
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,
},
};

View File

@ -1,25 +0,0 @@
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.',
}
}
},
},
};

Some files were not shown because too many files have changed in this diff Show More