diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 1329acb9f2..1c740b8ee7 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -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 diff --git a/db/export-data.sh b/db/export-data.sh index b50fc19e94..7a9c6e7c77 100755 --- a/db/export-data.sh +++ b/db/export-data.sh @@ -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 diff --git a/loopback/server/boot/print.js b/loopback/server/boot/print.js index 4a4996d2c0..0f6af4d56c 100644 --- a/loopback/server/boot/print.js +++ b/loopback/server/boot/print.js @@ -1,3 +1,3 @@ module.exports = function(app) { - require('../../../print/server.js')(app); + require('../../../print/boot.js')(app); }; diff --git a/loopback/util/http.js b/loopback/util/http.js new file mode 100644 index 0000000000..59bfe38b0e --- /dev/null +++ b/loopback/util/http.js @@ -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; +}; diff --git a/modules/claim/front/card/index.js b/modules/claim/front/card/index.js index 7c0c348a91..0a641ce89a 100644 --- a/modules/claim/front/card/index.js +++ b/modules/claim/front/card/index.js @@ -35,7 +35,7 @@ class Controller { { relation: 'client', scope: { - fields: ['salesPersonFk', 'name'], + fields: ['salesPersonFk', 'name', 'email'], include: { relation: 'salesPerson', scope: { diff --git a/modules/claim/front/descriptor/index.js b/modules/claim/front/descriptor/index.js index 0328d954c8..7bc9c831ae 100644 --- a/modules/claim/front/descriptor/index.js +++ b/modules/claim/front/descriptor/index.js @@ -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'), diff --git a/modules/claim/front/descriptor/index.spec.js b/modules/claim/front/descriptor/index.spec.js index 8cf8d1ea8a..87da181fae 100644 --- a/modules/claim/front/descriptor/index.spec.js +++ b/modules/claim/front/descriptor/index.spec.js @@ -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(); diff --git a/modules/client/back/models/client.js b/modules/client/back/models/client.js index 58aac9ccd1..73626b4083 100644 --- a/modules/client/back/models/client.js +++ b/modules/client/back/models/client.js @@ -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); } }); diff --git a/modules/client/front/sample/create/index.html b/modules/client/front/sample/create/index.html index 66c07397f1..25e2ff3208 100644 --- a/modules/client/front/sample/create/index.html +++ b/modules/client/front/sample/create/index.html @@ -1,4 +1,9 @@ + + - + + + + - - + +
+ +
+
diff --git a/modules/client/front/sample/create/index.js b/modules/client/front/sample/create/index.js index bb7ff21022..49d90f5698 100644 --- a/modules/client/front/sample/create/index.js +++ b/modules/client/front/sample/create/index.js @@ -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: '<' + } }); diff --git a/modules/client/front/sample/create/index.spec.js b/modules/client/front/sample/create/index.spec.js index 31e9bb4c3c..efcda54012 100644 --- a/modules/client/front/sample/create/index.spec.js +++ b/modules/client/front/sample/create/index.spec.js @@ -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(''); + 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(); diff --git a/modules/client/front/sample/create/style.scss b/modules/client/front/sample/create/style.scss index a958e264bd..fdd01f729b 100644 --- a/modules/client/front/sample/create/style.scss +++ b/modules/client/front/sample/create/style.scss @@ -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 } } -} \ No newline at end of file +} diff --git a/modules/route/front/card/index.js b/modules/route/front/card/index.js index 2e3acb9c10..76471002ae 100644 --- a/modules/route/front/card/index.js +++ b/modules/route/front/card/index.js @@ -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'] + } + } + } + } + } } ] }; diff --git a/modules/route/front/descriptor/index.js b/modules/route/front/descriptor/index.js index 438a2dda9a..402b9102f8 100644 --- a/modules/route/front/descriptor/index.js +++ b/modules/route/front/descriptor/index.js @@ -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'), diff --git a/modules/ticket/front/card/index.js b/modules/ticket/front/card/index.js index b05472bcbc..b805c3803a 100644 --- a/modules/ticket/front/card/index.js +++ b/modules/ticket/front/card/index.js @@ -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: { diff --git a/modules/ticket/front/descriptor/index.html b/modules/ticket/front/descriptor/index.html index 7ad5096337..12bd5105d5 100644 --- a/modules/ticket/front/descriptor/index.html +++ b/modules/ticket/front/descriptor/index.html @@ -197,7 +197,7 @@ \ No newline at end of file diff --git a/modules/ticket/front/descriptor/index.js b/modules/ticket/front/descriptor/index.js index beb95ee75e..d37d2d42bf 100644 --- a/modules/ticket/front/descriptor/index.js +++ b/modules/ticket/front/descriptor/index.js @@ -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'), diff --git a/modules/ticket/front/descriptor/index.spec.js b/modules/ticket/front/descriptor/index.spec.js index f1ebd55b7c..62aefa6cf3 100644 --- a/modules/ticket/front/descriptor/index.spec.js +++ b/modules/ticket/front/descriptor/index.spec.js @@ -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(``)($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'); diff --git a/package-lock.json b/package-lock.json index 21ccce9552..c39164e35b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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 } diff --git a/print/boot.js b/print/boot.js new file mode 100644 index 0000000000..4067c9e7b6 --- /dev/null +++ b/print/boot.js @@ -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)); + }); + }); +}; + + diff --git a/print/common/css/email.css b/print/common/css/email.css index 6b15fbcba4..a4489f6d29 100644 --- a/print/common/css/email.css +++ b/print/common/css/email.css @@ -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 } diff --git a/print/common/css/layout.css b/print/common/css/layout.css index 5914c35874..dc0c9b7b45 100644 --- a/print/common/css/layout.css +++ b/print/common/css/layout.css @@ -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 { diff --git a/print/common/css/misc.css b/print/common/css/misc.css index 16a37b6c3f..093d5a974e 100644 --- a/print/common/css/misc.css +++ b/print/common/css/misc.css @@ -1,3 +1,7 @@ +/** + * CSS misc classes + * +*/ .uppercase { text-transform: uppercase } diff --git a/print/common/css/report.css b/print/common/css/report.css index 24b20c3306..5b8a1539bf 100644 --- a/print/common/css/report.css +++ b/print/common/css/report.css @@ -1,5 +1,9 @@ +/** + * Report only stylesheet + * +*/ body { - zoom: 0.55 + zoom: 0.53 } .title { diff --git a/print/common/css/spacing.css b/print/common/css/spacing.css new file mode 100644 index 0000000000..670102d7cb --- /dev/null +++ b/print/common/css/spacing.css @@ -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; +} diff --git a/print/config/print.json b/print/config/print.json index 7ad2e13169..a075feed73 100755 --- a/print/config/print.json +++ b/print/config/print.json @@ -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", diff --git a/print/config/routes.json b/print/config/routes.json deleted file mode 100644 index 58d46193c1..0000000000 --- a/print/config/routes.json +++ /dev/null @@ -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"} -] \ No newline at end of file diff --git a/print/core/component.js b/print/core/component.js new file mode 100644 index 0000000000..836b8c9d9c --- /dev/null +++ b/print/core/component.js @@ -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; diff --git a/print/core/components/attachment/assets/css/import.js b/print/core/components/attachment/assets/css/import.js new file mode 100644 index 0000000000..c742fdf90a --- /dev/null +++ b/print/core/components/attachment/assets/css/import.js @@ -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(); diff --git a/print/core/components/attachment/assets/css/style.css b/print/core/components/attachment/assets/css/style.css new file mode 100644 index 0000000000..775c43ada9 --- /dev/null +++ b/print/core/components/attachment/assets/css/style.css @@ -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 +} \ No newline at end of file diff --git a/print/core/components/attachment/assets/images/attachment.svg b/print/core/components/attachment/assets/images/attachment.svg new file mode 100644 index 0000000000..2cb6303797 --- /dev/null +++ b/print/core/components/attachment/assets/images/attachment.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/print/core/components/attachment/attachment.html b/print/core/components/attachment/attachment.html new file mode 100644 index 0000000000..d66746e991 --- /dev/null +++ b/print/core/components/attachment/attachment.html @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/print/core/components/attachment/attachment.js b/print/core/components/attachment/attachment.js new file mode 100755 index 0000000000..2d4e74cdc5 --- /dev/null +++ b/print/core/components/attachment/attachment.js @@ -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 + } + } +}; + diff --git a/print/core/components/email-footer/assets/css/import.js b/print/core/components/email-footer/assets/css/import.js new file mode 100644 index 0000000000..c742fdf90a --- /dev/null +++ b/print/core/components/email-footer/assets/css/import.js @@ -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(); diff --git a/print/report/email-footer/assets/css/style.css b/print/core/components/email-footer/assets/css/style.css similarity index 68% rename from print/report/email-footer/assets/css/style.css rename to print/core/components/email-footer/assets/css/style.css index 9d47b193e1..6e6dc755bd 100644 --- a/print/report/email-footer/assets/css/style.css +++ b/print/core/components/email-footer/assets/css/style.css @@ -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 { diff --git a/print/report/email-footer/assets/images/action.png b/print/core/components/email-footer/assets/images/action.png similarity index 100% rename from print/report/email-footer/assets/images/action.png rename to print/core/components/email-footer/assets/images/action.png diff --git a/print/report/email-footer/assets/images/facebook.png b/print/core/components/email-footer/assets/images/facebook.png similarity index 100% rename from print/report/email-footer/assets/images/facebook.png rename to print/core/components/email-footer/assets/images/facebook.png diff --git a/print/report/email-footer/assets/images/info.png b/print/core/components/email-footer/assets/images/info.png similarity index 100% rename from print/report/email-footer/assets/images/info.png rename to print/core/components/email-footer/assets/images/info.png diff --git a/print/report/email-footer/assets/images/instagram.png b/print/core/components/email-footer/assets/images/instagram.png similarity index 100% rename from print/report/email-footer/assets/images/instagram.png rename to print/core/components/email-footer/assets/images/instagram.png diff --git a/print/report/email-footer/assets/images/linkedin.png b/print/core/components/email-footer/assets/images/linkedin.png similarity index 100% rename from print/report/email-footer/assets/images/linkedin.png rename to print/core/components/email-footer/assets/images/linkedin.png diff --git a/print/report/email-footer/assets/images/pinterest.png b/print/core/components/email-footer/assets/images/pinterest.png similarity index 100% rename from print/report/email-footer/assets/images/pinterest.png rename to print/core/components/email-footer/assets/images/pinterest.png diff --git a/print/report/email-footer/assets/images/twitter.png b/print/core/components/email-footer/assets/images/twitter.png similarity index 100% rename from print/report/email-footer/assets/images/twitter.png rename to print/core/components/email-footer/assets/images/twitter.png diff --git a/print/report/email-footer/assets/images/youtube.png b/print/core/components/email-footer/assets/images/youtube.png similarity index 100% rename from print/report/email-footer/assets/images/youtube.png rename to print/core/components/email-footer/assets/images/youtube.png diff --git a/print/core/components/email-footer/attachments.json b/print/core/components/email-footer/attachments.json new file mode 100644 index 0000000000..4f3bee667f --- /dev/null +++ b/print/core/components/email-footer/attachments.json @@ -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" + } +] \ No newline at end of file diff --git a/print/core/components/email-footer/email-footer.html b/print/core/components/email-footer/email-footer.html new file mode 100644 index 0000000000..5b2652cb18 --- /dev/null +++ b/print/core/components/email-footer/email-footer.html @@ -0,0 +1,53 @@ + \ No newline at end of file diff --git a/print/core/components/email-footer/email-footer.js b/print/core/components/email-footer/email-footer.js new file mode 100755 index 0000000000..527c8121af --- /dev/null +++ b/print/core/components/email-footer/email-footer.js @@ -0,0 +1,4 @@ +module.exports = { + name: 'email-footer', + props: ['isPreview', 'locale'] +}; diff --git a/print/core/components/email-footer/locale/es.yml b/print/core/components/email-footer/locale/es.yml new file mode 100644 index 0000000000..592a467377 --- /dev/null +++ b/print/core/components/email-footer/locale/es.yml @@ -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. diff --git a/print/core/components/email-footer/locale/fr.yml b/print/core/components/email-footer/locale/fr.yml new file mode 100644 index 0000000000..de4cf9095c --- /dev/null +++ b/print/core/components/email-footer/locale/fr.yml @@ -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. diff --git a/print/core/components/email-header/assets/css/import.js b/print/core/components/email-header/assets/css/import.js new file mode 100644 index 0000000000..c742fdf90a --- /dev/null +++ b/print/core/components/email-header/assets/css/import.js @@ -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(); diff --git a/print/core/components/email-header/assets/css/style.css b/print/core/components/email-header/assets/css/style.css new file mode 100644 index 0000000000..5d2f658ce7 --- /dev/null +++ b/print/core/components/email-header/assets/css/style.css @@ -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; +} \ No newline at end of file diff --git a/print/report/email-header/assets/images/email-logo.png b/print/core/components/email-header/assets/images/email-logo.png similarity index 100% rename from print/report/email-header/assets/images/email-logo.png rename to print/core/components/email-header/assets/images/email-logo.png diff --git a/print/core/components/email-header/assets/images/logo-black.png b/print/core/components/email-header/assets/images/logo-black.png new file mode 100644 index 0000000000..e93065d907 Binary files /dev/null and b/print/core/components/email-header/assets/images/logo-black.png differ diff --git a/print/report/report-header/assets/images/report-logo.svg b/print/core/components/email-header/assets/images/logo-black.svg similarity index 100% rename from print/report/report-header/assets/images/report-logo.svg rename to print/core/components/email-header/assets/images/logo-black.svg diff --git a/print/core/components/email-header/assets/images/logo-white.svg b/print/core/components/email-header/assets/images/logo-white.svg new file mode 100644 index 0000000000..a4ce32490d --- /dev/null +++ b/print/core/components/email-header/assets/images/logo-white.svg @@ -0,0 +1,131 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/print/core/components/email-header/attachments.json b/print/core/components/email-header/attachments.json new file mode 100644 index 0000000000..924eddc4d9 --- /dev/null +++ b/print/core/components/email-header/attachments.json @@ -0,0 +1,7 @@ +[ + { + "filename": "logo-black.png", + "path": "/assets/images/logo-black.png", + "cid": "logo-black.png" + } +] \ No newline at end of file diff --git a/print/core/components/email-header/email-header.html b/print/core/components/email-header/email-header.html new file mode 100644 index 0000000000..87834e2ae6 --- /dev/null +++ b/print/core/components/email-header/email-header.html @@ -0,0 +1,8 @@ +
+ +
+
diff --git a/print/core/components/email-header/email-header.js b/print/core/components/email-header/email-header.js new file mode 100755 index 0000000000..257770cadc --- /dev/null +++ b/print/core/components/email-header/email-header.js @@ -0,0 +1,4 @@ +module.exports = { + name: 'email-header', + props: ['locale'] +}; diff --git a/print/report/rpt-delivery-note/assets/css/index.js b/print/core/components/report-footer/assets/css/import.js similarity index 64% rename from print/report/rpt-delivery-note/assets/css/index.js rename to print/core/components/report-footer/assets/css/import.js index 515dea7503..a2a9334cb1 100644 --- a/print/report/rpt-delivery-note/assets/css/index.js +++ b/print/core/components/report-footer/assets/css/import.js @@ -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`, diff --git a/print/report/report-footer/assets/css/style.css b/print/core/components/report-footer/assets/css/style.css similarity index 100% rename from print/report/report-footer/assets/css/style.css rename to print/core/components/report-footer/assets/css/style.css diff --git a/print/core/components/report-footer/locale/es.yml b/print/core/components/report-footer/locale/es.yml new file mode 100644 index 0000000000..8c54b796f3 --- /dev/null +++ b/print/core/components/report-footer/locale/es.yml @@ -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. diff --git a/print/core/components/report-footer/locale/fr.yml b/print/core/components/report-footer/locale/fr.yml new file mode 100644 index 0000000000..e35f9fb7ff --- /dev/null +++ b/print/core/components/report-footer/locale/fr.yml @@ -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. diff --git a/print/core/components/report-footer/locale/pt.yml b/print/core/components/report-footer/locale/pt.yml new file mode 100644 index 0000000000..8494c1ed53 --- /dev/null +++ b/print/core/components/report-footer/locale/pt.yml @@ -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. diff --git a/print/report/report-footer/index.html b/print/core/components/report-footer/report-footer.html similarity index 63% rename from print/report/report-footer/index.html rename to print/core/components/report-footer/report-footer.html index 11b314af01..af433676f7 100644 --- a/print/report/report-footer/index.html +++ b/print/core/components/report-footer/report-footer.html @@ -1,7 +1,7 @@