diff --git a/.vscode/launch.json b/.vscode/launch.json index ecebe6bce..b35ebd9e4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,26 +1,6 @@ { "version": "0.2.0", "configurations": [ - { - "name": "Iniciar", - "type": "node", - "request": "launch", - "program": "${workspaceRoot}/services/auth/server/server.js", - "stopOnEntry": false, - "args": [], - "cwd": "${workspaceRoot}", - "preLaunchTask": null, - "runtimeExecutable": null, - "runtimeArgs": [ - "--nolazy" - ], - "env": { - "NODE_ENV": "development" - }, - "console": "internalConsole", - "sourceMaps": false, - "outFiles": [] - }, { "name": "Asociar", "type": "node", diff --git a/client/client/routes.json b/client/client/routes.json index c938dd8a6..99c083ff3 100644 --- a/client/client/routes.json +++ b/client/client/routes.json @@ -26,7 +26,7 @@ "client": "$ctrl.client" }, "menu": { - "description": "Datos básicos", + "description": "Basic data", "icon": "person" } }, { @@ -37,7 +37,7 @@ "client": "$ctrl.client" }, "menu": { - "description": "Datos fiscales", + "description": "Fiscal data", "icon": "account_balance" } }, { @@ -64,7 +64,7 @@ "client": "$ctrl.client" }, "menu": { - "description": "Consignatarios", + "description": "Addresses", "icon": "local_shipping" } }, { @@ -83,7 +83,7 @@ "client": "$ctrl.client" }, "menu": { - "description": "Acceso web", + "description": "Web access", "icon": "language" } }, { @@ -99,13 +99,59 @@ "client": "$ctrl.client" }, "menu": { - "description": "Notas", + "description": "Notes", "icon": "insert_drive_file" } }, { "url": "/create", "state": "clientCard.notes.create", "component": "vn-note-create" + }, { + "url": "/credit", + "abstract": true, + "state": "clientCard.credit", + "component": "ui-view" + }, { + "url": "/list", + "state": "clientCard.credit.list", + "component": "vn-client-credit-list", + "params": { + "client": "$ctrl.client" + }, + "menu": { + "description": "Credit", + "icon": "credit_card" + } + }, { + "url": "/create", + "state": "clientCard.credit.create", + "component": "vn-client-credit-create", + "params": { + "client": "$ctrl.client" + } + }, { + "url": "/greuge", + "abstract": true, + "state": "clientCard.greuge", + "component": "ui-view" + }, { + "url": "/list", + "state": "clientCard.greuge.list", + "component": "vn-client-greuge-list", + "params": { + "client": "$ctrl.client" + }, + "menu": { + "description": "Greuge", + "icon": "work" + } + }, { + "url": "/create", + "state": "clientCard.greuge.create", + "component": "vn-client-greuge-create", + "params": { + "client": "$ctrl.client" + } } ] } diff --git a/client/client/src/addresses/locale/es.json b/client/client/src/addresses/locale/es.json index dc7a13c4a..ab0c0e4ca 100644 --- a/client/client/src/addresses/locale/es.json +++ b/client/client/src/addresses/locale/es.json @@ -1,4 +1,3 @@ { - "Addresses": "Consignatarios", "Set as default": "Establecer como predeterminado" } \ No newline at end of file diff --git a/client/client/src/basic-data/locale/es.json b/client/client/src/basic-data/locale/es.json index d0e654979..4594d683e 100644 --- a/client/client/src/basic-data/locale/es.json +++ b/client/client/src/basic-data/locale/es.json @@ -1,5 +1,4 @@ { - "Basic data": "Datos básicos", "Comercial Name": "Nombre comercial", "Tax number": "NIF/CIF", "Social name": "Razón social", diff --git a/client/client/src/billing-data/billing-data.html b/client/client/src/billing-data/billing-data.html index 19b961558..c46c00e98 100644 --- a/client/client/src/billing-data/billing-data.html +++ b/client/client/src/billing-data/billing-data.html @@ -21,10 +21,6 @@ - - - - diff --git a/client/client/src/billing-data/billing-data.js b/client/client/src/billing-data/billing-data.js index a6887039e..6bbb3705c 100644 --- a/client/client/src/billing-data/billing-data.js +++ b/client/client/src/billing-data/billing-data.js @@ -17,9 +17,6 @@ export default class Controller { this.billData.payMethodFk = this.client.payMethodFk; this.billData.iban = this.client.iban; this.billData.dueDay = this.client.dueDay; - this.billData.discount = this.client.discount; - this.billData.credit = this.client.credit; - this.billData.creditInsurance = this.client.creditInsurance; } } submit() { @@ -43,7 +40,7 @@ export default class Controller { } returnDialog(response) { if (response === 'ACCEPT') { - this.$http.post(`/mailer/manuscript/payment-update/${this.client.id}`).then( + this.$http.post(`/mailer/notification/payment-update/${this.client.id}`).then( () => { this.vnApp.showMessage(this.translate.instant('Notification sent!')); } diff --git a/client/client/src/billing-data/billing-data.spec.js b/client/client/src/billing-data/billing-data.spec.js index df169b18c..85980aa80 100644 --- a/client/client/src/billing-data/billing-data.spec.js +++ b/client/client/src/billing-data/billing-data.spec.js @@ -25,9 +25,6 @@ describe('Client', () => { describe('copyData()', () => { it(`should define billData using client's data`, () => { controller.client = { - credit: 1000000000000, - creditInsurance: null, - discount: 99, dueDay: 0, iban: null, payMethodFk: 1 @@ -72,8 +69,8 @@ describe('Client', () => { describe('returnDialog()', () => { it('should request to send notification email', () => { controller.client = {id: '123'}; - $httpBackend.when('POST', `/mailer/manuscript/payment-update/${controller.client.id}`).respond('done'); - $httpBackend.expectPOST(`/mailer/manuscript/payment-update/${controller.client.id}`); + $httpBackend.when('POST', `/mailer/notification/payment-update/${controller.client.id}`).respond('done'); + $httpBackend.expectPOST(`/mailer/notification/payment-update/${controller.client.id}`); controller.returnDialog('ACCEPT'); $httpBackend.flush(); }); diff --git a/client/client/src/client.js b/client/client/src/client.js index 6dc0de725..15bd2d8ec 100644 --- a/client/client/src/client.js +++ b/client/client/src/client.js @@ -14,3 +14,7 @@ import './address-edit/address-edit'; import './notes/notes'; import './note-create/note-create'; import './web-access/web-access'; +import './credit-list/credit-list'; +import './credit-create/credit-create'; +import './greuge-list/greuge-list'; +import './greuge-create/greuge-create'; diff --git a/client/client/src/credit-create/credit-create.html b/client/client/src/credit-create/credit-create.html new file mode 100644 index 000000000..c02ff7167 --- /dev/null +++ b/client/client/src/credit-create/credit-create.html @@ -0,0 +1,23 @@ + + + +
+ + + Add credit + + + + + + + + + + + +
diff --git a/client/client/src/credit-create/credit-create.js b/client/client/src/credit-create/credit-create.js new file mode 100644 index 000000000..2d0bccf56 --- /dev/null +++ b/client/client/src/credit-create/credit-create.js @@ -0,0 +1,8 @@ +import ngModule from '../module'; + +ngModule.component('vnClientCreditCreate', { + template: require('./credit-create.html'), + bindings: { + client: '<' + } +}); diff --git a/client/client/src/credit-create/locale/es.json b/client/client/src/credit-create/locale/es.json new file mode 100644 index 000000000..7b0a22a1f --- /dev/null +++ b/client/client/src/credit-create/locale/es.json @@ -0,0 +1,3 @@ +{ + "Add credit": "Añadir crédito" +} \ No newline at end of file diff --git a/client/client/src/credit-list/credit-list.html b/client/client/src/credit-list/credit-list.html new file mode 100644 index 000000000..30a477cef --- /dev/null +++ b/client/client/src/credit-list/credit-list.html @@ -0,0 +1,29 @@ + + + + Credit + + + + + + + + + {{::credit.amount | number:2}} € + {{::credit.created | date:'dd/MM/yyyy HH:mm' }} + {{::credit.employee.name}} + + + No results + + + + + + + \ No newline at end of file diff --git a/client/client/src/credit-list/credit-list.js b/client/client/src/credit-list/credit-list.js new file mode 100644 index 000000000..c1d283e33 --- /dev/null +++ b/client/client/src/credit-list/credit-list.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import FilterClientList from '../filterClientList'; + +class ClientCreditList extends FilterClientList {} + +ngModule.component('vnClientCreditList', { + template: require('./credit-list.html'), + controller: ClientCreditList +}); diff --git a/client/client/src/credit-list/locale/es.json b/client/client/src/credit-list/locale/es.json new file mode 100644 index 000000000..2c99e8b73 --- /dev/null +++ b/client/client/src/credit-list/locale/es.json @@ -0,0 +1,5 @@ +{ + "Since" : "Desde", + "Employee" : "Empleado", + "No results": "Sin resultados" +} \ No newline at end of file diff --git a/client/client/src/filterClientList.js b/client/client/src/filterClientList.js new file mode 100644 index 000000000..3ad70cfc5 --- /dev/null +++ b/client/client/src/filterClientList.js @@ -0,0 +1,37 @@ +export default class FilterClientList { + constructor($scope, $timeout, $state) { + this.$ = $scope; + this.$timeout = $timeout; + this.$state = $state; + + this.waitingMgCrud = 0; + this.clientFk = $state.params.id; + } + onOrder(field, order) { + this.filter(`${field} ${order}`); + } + filter(order) { + if (this.$.index && this.clientFk) { + this.waitingMgCrud = 0; + this.$.index.filter = { + page: 1, + size: 10, + clientFk: this.clientFk + }; + + if (order) { + this.$.index.filter.order = order; + } + + this.$.index.accept(); + } else if (this.waitingMgCrud > 3) { + throw new Error('Magic Crud is not loaded'); + } else { + this.waitingMgCrud++; + this.$timeout(() => { + this.filter(order); + }, 250); + } + } +} +FilterClientList.$inject = ['$scope', '$timeout', '$state']; diff --git a/client/client/src/greuge-create/greuge-create.html b/client/client/src/greuge-create/greuge-create.html new file mode 100644 index 000000000..9bc8830dd --- /dev/null +++ b/client/client/src/greuge-create/greuge-create.html @@ -0,0 +1,30 @@ + + + +
+ + + Add Greuge + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/client/client/src/greuge-create/greuge-create.js b/client/client/src/greuge-create/greuge-create.js new file mode 100644 index 000000000..f48c3e61b --- /dev/null +++ b/client/client/src/greuge-create/greuge-create.js @@ -0,0 +1,23 @@ +import ngModule from '../module'; + +class ClientGreugeCreate { + constructor($scope, $state) { + this.$ = $scope; + this.$state = $state; + this.greuge = {}; + } + onSubmit() { + this.greuge.clientFk = this.$state.params.id; + this.$.watcher.submit().then( + () => { + this.$state.go('clientCard.greuge.list'); + } + ); + } +} +ClientGreugeCreate.$inject = ['$scope', '$state']; + +ngModule.component('vnClientGreugeCreate', { + template: require('./greuge-create.html'), + controller: ClientGreugeCreate +}); diff --git a/client/client/src/greuge-list/greuge-list.html b/client/client/src/greuge-list/greuge-list.html new file mode 100644 index 000000000..08c774890 --- /dev/null +++ b/client/client/src/greuge-list/greuge-list.html @@ -0,0 +1,38 @@ + + + + + Greuge + + + + + + + + + + {{::greuge.shipped | date:'dd/MM/yyyy HH:mm' }} + {{::greuge.description}} + {{::greuge.amount | number:2}} € + {{::greuge.greugeType.name}} + + + + No results + + + + {{edit.model.sumAmount | number:2}} € + + + + + + + + \ No newline at end of file diff --git a/client/client/src/greuge-list/greuge-list.js b/client/client/src/greuge-list/greuge-list.js new file mode 100644 index 000000000..a2e5bd33a --- /dev/null +++ b/client/client/src/greuge-list/greuge-list.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import FilterClientList from '../filterClientList'; + +class ClientGreugeList extends FilterClientList {} + +ngModule.component('vnClientGreugeList', { + template: require('./greuge-list.html'), + controller: ClientGreugeList +}); diff --git a/client/client/src/greuge-list/locale/es.json b/client/client/src/greuge-list/locale/es.json new file mode 100644 index 000000000..7aa276bd0 --- /dev/null +++ b/client/client/src/greuge-list/locale/es.json @@ -0,0 +1,7 @@ +{ + "Date" : "Fecha", + "Comment" : "Comentario", + "Amount" : "Importe", + "Type": "Tipo", + "Add Greuge": "Añadir Greuge" +} \ No newline at end of file diff --git a/client/client/src/locale/es.json b/client/client/src/locale/es.json deleted file mode 100644 index 3e3e631ea..000000000 --- a/client/client/src/locale/es.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "Active": "Activo", - "Client": "Cliente", - "Clients": "Clientes", - "Fiscal data": "Datos Fiscales", - "Has to invoice": "Factura", - "Invoice by mail": "Factura impresa", - "Country": "País", - "Street": "Domicilio fiscal", - "City": "Municipio", - "Postcode": "Código postal", - "Province": "Provincia", - "Save": "Guardar", - "Pay method" : "Forma de pago", - "Address": "Consignatario", - "Credit": "Crédito" -} diff --git a/client/client/src/notes/locale/es.json b/client/client/src/notes/locale/es.json deleted file mode 100644 index 28b37f5a8..000000000 --- a/client/client/src/notes/locale/es.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Notes": "Notas" -} \ No newline at end of file diff --git a/client/client/src/web-access/locale/es.json b/client/client/src/web-access/locale/es.json index 82652f89f..dd42d1c3a 100644 --- a/client/client/src/web-access/locale/es.json +++ b/client/client/src/web-access/locale/es.json @@ -1,7 +1,6 @@ { "User": "Usuario", "Enable web access": "Habilitar acceso web", - "Web access": "Acceso web", "New password": "Nueva contraseña", "Repeat password": "Repetir contraseña", "Change password": "Cambiar contraseña" diff --git a/client/core/src/column-header/column-header.html b/client/core/src/column-header/column-header.html index 435057a19..d5747fcfe 100644 --- a/client/core/src/column-header/column-header.html +++ b/client/core/src/column-header/column-header.html @@ -1,5 +1,5 @@
- + {{::$ctrl.text}} diff --git a/client/core/src/column-header/column-header.js b/client/core/src/column-header/column-header.js index 2f5f4d3c3..4c52e7e3d 100644 --- a/client/core/src/column-header/column-header.js +++ b/client/core/src/column-header/column-header.js @@ -1,19 +1,25 @@ import {module} from '../module'; export default class ColumnHeader { - constructor() { + constructor($attrs) { this.order = undefined; this.mouseIsOver = false; + this.orderLocked = ($attrs.orderLocked !== undefined); } onClick() { - if (this.order === 'ASC') { - this.order = 'DESC'; - } else { - this.order = 'ASC'; + if (!this.orderLocked) { + if (this.order === 'ASC') { + this.order = 'DESC'; + } else { + this.order = 'ASC'; + } + this.gridHeader.selectColum(this); } - this.gridHeader.selectColum(this); } showArrow(type) { + if (this.orderLocked) + return false; + let showArrow = (this.gridHeader && this.gridHeader.currentColumn && this.gridHeader.currentColumn.field === this.field && this.order === type); let showOther = (this.gridHeader && this.gridHeader.currentColumn && this.gridHeader.currentColumn.field === this.field && this.order !== type); if (type === 'DESC' && this.mouseIsOver && !showOther) { @@ -22,13 +28,13 @@ export default class ColumnHeader { return showArrow; } $onInit() { - if (this.defaultOrder) { + if (this.defaultOrder && !this.orderLocked) { this.order = this.defaultOrder; this.onClick(); } } } -ColumnHeader.$inject = []; +ColumnHeader.$inject = ['$attrs']; module.component('vnColumnHeader', { template: require('./column-header.html'), diff --git a/client/core/src/column-header/column-header.spec.js b/client/core/src/column-header/column-header.spec.js index 5c6d29d73..48954826e 100644 --- a/client/core/src/column-header/column-header.spec.js +++ b/client/core/src/column-header/column-header.spec.js @@ -3,6 +3,7 @@ import './column-header.js'; describe('Component vnColumnHeader', () => { let $componentController; let controller; + let $attrs; beforeEach(() => { angular.mock.module('client'); @@ -10,7 +11,8 @@ describe('Component vnColumnHeader', () => { beforeEach(angular.mock.inject(_$componentController_ => { $componentController = _$componentController_; - controller = $componentController('vnColumnHeader', {}); + $attrs = {}; + controller = $componentController('vnColumnHeader', {$attrs}); })); describe('onClick()', () => { diff --git a/client/core/src/datePicker/datePicker.html b/client/core/src/datePicker/datePicker.html index d91f0a53f..21839aa16 100644 --- a/client/core/src/datePicker/datePicker.html +++ b/client/core/src/datePicker/datePicker.html @@ -4,7 +4,8 @@ ng-focus="$ctrl.hasFocus = true" ng-blur="$ctrl.hasFocus = false" ng-mouseenter="$ctrl.hasMouseIn = true" - ng-mouseleave="$ctrl.hasMouseIn = false" + ng-mouseleave="$ctrl.hasMouseIn = false" + ng-click="$ctrl.onClick()" > { }); describe('$onChanges()', () => { - it(`should set the top css of the $element`, () => { - let argumentObject = {show: true, top: {currentValue: 100}}; - spyOn(controller.$element, 'css'); - controller.$onChanges(argumentObject); - - expect(controller.$element.css).toHaveBeenCalledWith('top', '100px'); - }); - it(`should set the width css of the $element`, () => { let argumentObject = {show: true, itemWidth: {currentValue: 100}}; spyOn(controller.$element, 'css'); @@ -295,26 +287,40 @@ describe('Component vnDropDown', () => { controller.selectItem(item); expect(controller.selected).toEqual(item); - expect(controller._show).toEqual(true); + expect(controller._show).not.toBeDefined(); }); }); describe('loadItems()', () => { - it(`should set controller._show to true`, () => { + it(`should set controller.show to true`, () => { + controller.show = false; + controller.itemsFiltered = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}]; controller.loadItems(); - expect(controller._show).toEqual(true); + expect(controller.show).toEqual(true); }); - it(`should call loadMore() and then set controller._show to true`, () => { + it(`should call loadMore() and then set controller._show to true if there are items`, () => { controller.showLoadMore = () => {}; controller.loadMore = () => {}; spyOn(controller, 'loadMore'); + controller.itemsFiltered = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}]; controller.loadItems(); expect(controller._show).toEqual(true); expect(controller.loadMore).toHaveBeenCalledWith(); }); + + it(`should call loadMore() and then set controller._show to undefined if there are not items`, () => { + controller.showLoadMore = () => {}; + controller.loadMore = () => {}; + spyOn(controller, 'loadMore'); + controller.itemsFiltered = []; + controller.loadItems(); + + expect(controller._show).not.toBeDefined(); + expect(controller.loadMore).toHaveBeenCalledWith(); + }); }); describe('$onInit()', () => { diff --git a/client/core/src/grid-header/style.scss b/client/core/src/grid-header/style.scss index cc983dcf1..324e488d1 100644 --- a/client/core/src/grid-header/style.scss +++ b/client/core/src/grid-header/style.scss @@ -2,7 +2,6 @@ vn-grid-header { border-bottom: 3px solid #9D9D9D; font-weight: bold; .orderly{ - cursor: pointer; text-align: center; white-space: nowrap; justify-content: center; diff --git a/client/core/src/watcher/watcher.js b/client/core/src/watcher/watcher.js index 2dbc5a2bc..d259f6f39 100644 --- a/client/core/src/watcher/watcher.js +++ b/client/core/src/watcher/watcher.js @@ -68,9 +68,9 @@ export default class Watcher extends Component { * * @param {String} state The state name */ - submitGo(state) { + submitGo(state, params) { return this.submit().then( - () => this.$state.go(state) + () => this.$state.go(state, params || {}) ); } /** diff --git a/client/core/src/watcher/watcher.spec.js b/client/core/src/watcher/watcher.spec.js index 0ed342e31..7cd629cd8 100644 --- a/client/core/src/watcher/watcher.spec.js +++ b/client/core/src/watcher/watcher.spec.js @@ -100,11 +100,11 @@ describe('Component vnWatcher', () => { it(`should call controller.$state.go() function after calling controllers submit() function`, done => { spyOn(controller, 'submit').and.returnValue(Promise.resolve()); spyOn(controller.$state, 'go'); - let state = 'the state'; + let state = 'the.State'; controller.submitGo(state) .then(() => { expect(controller.submit).toHaveBeenCalledWith(); - expect(controller.$state.go).toHaveBeenCalledWith(state); + expect(controller.$state.go).toHaveBeenCalledWith(state, {}); done(); }); }); diff --git a/client/production/src/production-table/production-table.js b/client/production/src/production-table/production-table.js index 91c24314f..288118a0d 100644 --- a/client/production/src/production-table/production-table.js +++ b/client/production/src/production-table/production-table.js @@ -13,6 +13,8 @@ export class ProductionTable { }, model: [] }; + this.filteredField = null; + this.filteredReverse = null; } get checkAll() { return this._checkAll; @@ -21,7 +23,7 @@ export class ProductionTable { this._checkAll = value; } set tickets(value) { - this._tickets = value; + this._tickets = this.filteredField ? this.$filter('orderBy')(value, this.filteredField, this.filteredReverse) : value; this.totalFilter = this._tickets.length; this.pageTable.filter.page = 1; this.pageTickets(); @@ -30,8 +32,9 @@ export class ProductionTable { return this._tickets; } onOrder(field, order) { - let reverse = order === 'DESC'; - this.tickets = this.$filter('orderBy')(this.tickets, field, reverse); + this.filteredField = field; + this.filteredReverse = order === 'DESC'; + this.tickets = this.tickets; // call tickets setter } pageTickets() { let init = (this.pageTable.filter.page - 1) * this.itemsDisplayedInList; diff --git a/client/salix/src/components/left-menu/actions.js b/client/salix/src/components/left-menu/actions.js index 4d4d6ee6d..30d379df1 100644 --- a/client/salix/src/components/left-menu/actions.js +++ b/client/salix/src/components/left-menu/actions.js @@ -9,7 +9,7 @@ export default class MenuActions { switchItem() { if (!this.items || !this.items.length) return; - let stateName = this.$state.current.name.replace('create', 'list').replace('edit', 'list'); + let stateName = this.$state.current.name.replace('.create', '.list').replace('.edit', '.list'); for (let i = 0; i < this.items.length; i++) { this.items[i].active = (this.items[i].href === stateName); diff --git a/client/salix/src/configroutes.js b/client/salix/src/configroutes.js index 1d79c5bc2..e5ab72fb8 100644 --- a/client/salix/src/configroutes.js +++ b/client/salix/src/configroutes.js @@ -47,9 +47,8 @@ function config($stateProvider, $urlRouterProvider, aclServiceProvider, modulesF for (let i = 0; i < count; i++) { let route = fileRoutes[i]; if (aclService.routeHasPermission(route)) { - $stateProvider.state(route.state, { + let configRoute = { url: route.url, - abstract: route.abstract || false, template: `<${route.component} ${getParams(route)}>`, resolve: { loader: loader(moduleName, validations) @@ -57,7 +56,13 @@ function config($stateProvider, $urlRouterProvider, aclServiceProvider, modulesF data: { routes: fileRoutes } - }); + }; + if (route.abstract) + configRoute.abstract = true; + if (route.routeParams) + configRoute.params = route.routeParams; + + $stateProvider.state(route.state, configRoute); } else if (route.state === mainModule.state) { break; } diff --git a/services/auth/server/boot/routes.js b/services/auth/server/boot/routes.js index 9aa76896b..3812a50fb 100644 --- a/services/auth/server/boot/routes.js +++ b/services/auth/server/boot/routes.js @@ -88,29 +88,4 @@ module.exports = function(app) { User.logout(req.accessToken.id, () => res.redirect('/')); }); - - app.get('/validateToken', function(req, res) { - let token = req.headers.authorization; - - validateToken(token, function(isValid) { - if (isValid) { - res.status(200); - } else { - res.status(401).json({ - message: 'Invalid token' - }); - } - }); - }); - - function validateToken(tokenId, cb) { - app.models.AccessToken.findById(tokenId, function(err, token) { - if (token) { - token.validate(function (err, isValid) { - cb(isValid === true, token); - }); - } else - cb(false); - }); - } }; diff --git a/services/client/common/methods/client/activate.js b/services/client/common/methods/client/activate.js index 9d78d0d63..279baffbf 100644 --- a/services/client/common/methods/client/activate.js +++ b/services/client/common/methods/client/activate.js @@ -49,7 +49,7 @@ module.exports = function(Client) { method: 'POST', headers: { 'content-type': 'application/json', - 'authorization': ctx.req.headers.authorization + 'Authorization': ctx.req.headers.authorization }, json: {} }; diff --git a/services/client/common/methods/client/before-save.js b/services/client/common/methods/client/before-save.js index 470b7ba3f..b761a71f1 100644 --- a/services/client/common/methods/client/before-save.js +++ b/services/client/common/methods/client/before-save.js @@ -116,12 +116,7 @@ module.exports = function(Client) { } function maxCb(_, instances) { - if (!instances) { - done(generateErrorCredit()); - return; - } - - if (instances.length !== 1 || instances[0].employeeFk == userId || instances[0].amount > 0) { + if (!instances || instances.length !== 1 || instances[0].employeeFk == userId || instances[0].amount > 0) { done(); return; } diff --git a/services/client/common/methods/greuge/filter.js b/services/client/common/methods/greuge/filter.js new file mode 100644 index 000000000..dba4e8ef6 --- /dev/null +++ b/services/client/common/methods/greuge/filter.js @@ -0,0 +1,20 @@ +module.exports = Self => { + Self.installMethod('filter', filterParams); + + function filterParams(params) { + return { + where: { + clientFk: params.clientFk + }, + skip: (params.page - 1) * params.size, + limit: params.size, + order: params.order || 'shipped DESC', + include: { + relation: "greugeType", + scope: { + fields: ["id", "name"] + } + } + }; + } +}; diff --git a/services/client/common/methods/greuge/totalGreuge.js b/services/client/common/methods/greuge/totalGreuge.js new file mode 100644 index 000000000..849746493 --- /dev/null +++ b/services/client/common/methods/greuge/totalGreuge.js @@ -0,0 +1,34 @@ +module.exports = Self => { + Self.remoteMethod('sumAmount', { + description: 'returns sum greuge.ammount from client', + accessType: 'READ', + accepts: [{ + arg: 'id', + type: 'number', + required: true, + description: 'clientFk', + http: {source: 'path'} + }], + returns: { + arg: 'sumAmount' + }, + http: { + path: `/:id/sumAmount`, + verb: 'get' + } + }); + + Self.sumAmount = (clientFk, callback) => { + let query = `SELECT sum(amount) as sumAmount FROM vn.greuge WHERE clientFk = ?`; + Self.rawSql(query, [clientFk], callback).then(response => { + if (response.length) { + callback(null, response[0].sumAmount); + } else { + callback(null, 0); + } + }) + .catch(reject => { + callback(reject, null); + }); + }; +}; diff --git a/services/client/common/models/client-credit.js b/services/client/common/models/client-credit.js new file mode 100644 index 000000000..7205be03e --- /dev/null +++ b/services/client/common/models/client-credit.js @@ -0,0 +1,31 @@ +module.exports = function(Self) { + Self.installMethod('filter', filterParams, filterResults); + + function filterParams(params) { + return { + where: { + clientFk: params.clientFk + }, + skip: (params.page - 1) * params.size, + limit: params.size, + order: params.order || 'created DESC', + include: { + relation: "employee", + scope: { + fields: ["id", "name", "surname"] + } + } + }; + } + + function filterResults(instances) { + let result = JSON.parse(JSON.stringify(instances)); + if (result && result.instances && result.instances.length) { + result.instances.forEach((element, i) => { + result.instances[i].employee.name = `${element.employee.name} ${element.employee.surname}`; + delete result.instances[i].employee.surname; + }); + } + return result; + } +}; diff --git a/services/client/common/models/client-credit.json b/services/client/common/models/client-credit.json index f6512bfc7..26a1733ad 100644 --- a/services/client/common/models/client-credit.json +++ b/services/client/common/models/client-credit.json @@ -1,5 +1,5 @@ { - "name": "ClientCredit", + "name": "clientCredit", "base": "VnModel", "validateUpsert": true, "properties": { diff --git a/services/client/common/models/greuge-type.json b/services/client/common/models/greuge-type.json new file mode 100644 index 000000000..151aea5a6 --- /dev/null +++ b/services/client/common/models/greuge-type.json @@ -0,0 +1,14 @@ +{ + "name": "greugeType", + "base": "VnModel", + "properties": { + "id": { + "id": true, + "type": "Number", + "description": "Identifier" + }, + "name": { + "type": "String" + } + } + } \ No newline at end of file diff --git a/services/client/common/models/greuge.js b/services/client/common/models/greuge.js new file mode 100644 index 000000000..7bacecc4e --- /dev/null +++ b/services/client/common/models/greuge.js @@ -0,0 +1,4 @@ +module.exports = function(Self) { + require('../methods/greuge/filter.js')(Self); + require('../methods/greuge/totalGreuge.js')(Self); +}; diff --git a/services/client/common/models/greuge.json b/services/client/common/models/greuge.json new file mode 100644 index 000000000..2c00f9f73 --- /dev/null +++ b/services/client/common/models/greuge.json @@ -0,0 +1,35 @@ +{ + "name": "greuge", + "base": "VnModel", + "properties": { + "id": { + "id": true, + "type": "Number", + "description": "Identifier" + }, + "description": { + "type": "String" + }, + "amount": { + "type": "Number" + }, + "shipped": { + "type": "date" + }, + "created": { + "type": "date" + } + }, + "relations": { + "client": { + "type": "belongsTo", + "model": "Client", + "foreignKey": "clientFk" + }, + "greugeType": { + "type": "belongsTo", + "model": "greugeType", + "foreignKey": "greugeTypeFk" + } + } + } \ No newline at end of file diff --git a/services/client/common/validations/validateDni.js b/services/client/common/validations/validateDni.js index ab9008605..94b595f09 100644 --- a/services/client/common/validations/validateDni.js +++ b/services/client/common/validations/validateDni.js @@ -1,4 +1,7 @@ module.exports = fi => { + if (fi === undefined || fi === null) { + return true; + } let dni = fi; let getLetterDni = dni => { const regExpDni = 'TRWAGMYFPDXBNJZSQVHLCKE'; diff --git a/services/client/server/model-config.json b/services/client/server/model-config.json index 3c9c2199d..c95fb15c7 100644 --- a/services/client/server/model-config.json +++ b/services/client/server/model-config.json @@ -27,8 +27,8 @@ "Client": { "dataSource": "vn" }, - "ClientCredit": { - "dataSource": "salix" + "clientCredit": { + "dataSource": "vn" }, "ClientCreditLimit": { "dataSource": "salix" @@ -59,5 +59,11 @@ }, "CreditClassification": { "dataSource": "salix" + }, + "greuge": { + "dataSource": "vn" + }, + "greugeType": { + "dataSource": "vn" } } diff --git a/services/loopback/common/models/vn-model.js b/services/loopback/common/models/vn-model.js index 3305cb67d..30f358204 100644 --- a/services/loopback/common/models/vn-model.js +++ b/services/loopback/common/models/vn-model.js @@ -130,7 +130,7 @@ module.exports = function(Self) { }; }; - Self.installMethod = function(methodName, filterCb) { + Self.installMethod = function(methodName, filterCb, filterResult) { this.remoteMethod(methodName, { description: 'List items using a filter', accessType: 'READ', @@ -161,8 +161,12 @@ module.exports = function(Self) { var response = {}; function returnValues() { - if (response.instances !== undefined && response.count !== undefined) - cb(null, response); + if (response.instances !== undefined && response.count !== undefined) { + if (filterResult) + cb(null, filterResult(response)); + else + cb(null, response); + } } function error() { diff --git a/services/mailer/application/auth.js b/services/mailer/application/auth.js index ee8f114e1..7e9457537 100644 --- a/services/mailer/application/auth.js +++ b/services/mailer/application/auth.js @@ -1,4 +1,5 @@ var database = require('./database.js'); +let config = require('./config.js'); module.exports = { @@ -31,7 +32,23 @@ module.exports = { if (this.isTokenExpired(token.created, token.ttl)) return this.response.status(401).send({message: 'Token expired'}); - this.request.userId = token.userId; + // Set proxy host + let host = this.request.headers.host.split(':')[0]; + let proxy; + + if (host == '127.0.0.1') + proxy = config.proxy.localhost; + else if (process.env.NODE_ENV == 'production') + proxy = config.proxy.salix; + else if (process.env.NODE_ENV == 'development') + proxy = config.proxy.testSalix; + + this.request.proxyHost = `http://${proxy.host}:${proxy.port}`; + this.request.user = { + id: token.userId, + token: this.getToken() + } + this.next(); }); }, diff --git a/services/mailer/application/config.js b/services/mailer/application/config.js new file mode 100644 index 000000000..1442e85ed --- /dev/null +++ b/services/mailer/application/config.js @@ -0,0 +1,18 @@ +var path = require('path'); +var fs = require('fs'); +var config = {}; + +let devConfigPath = path.join(__dirname, '/config/datasources.development.json'); +let configPath = path.join(__dirname, '/config/datasources.json'); + +try { + config = Object.assign(require(configPath), require(devConfigPath)); +} catch (e) { + if (e.code == 'MODULE_NOT_FOUND') + config = require(configPath); +} + +config.proxy = require('../../nginx/config.json'); +config.package = require('../package.json'); + +module.exports = config; \ No newline at end of file diff --git a/services/mailer/application/config/app.json b/services/mailer/application/config/app.json deleted file mode 100644 index c9ec4174b..000000000 --- a/services/mailer/application/config/app.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "MailServer", - "version": "1.0.0", - "port": 3003, - "debug": false, - "defaultLanguage": "es", - "senderMail": "noreply@localhost", - "senderName": "" -} \ No newline at end of file diff --git a/services/mailer/application/config/app.test.json b/services/mailer/application/config/app.test.json deleted file mode 100644 index c9ec4174b..000000000 --- a/services/mailer/application/config/app.test.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "MailServer", - "version": "1.0.0", - "port": 3003, - "debug": false, - "defaultLanguage": "es", - "senderMail": "noreply@localhost", - "senderName": "" -} \ No newline at end of file diff --git a/services/mailer/application/config/datasources.json b/services/mailer/application/config/datasources.json new file mode 100644 index 000000000..69943439d --- /dev/null +++ b/services/mailer/application/config/datasources.json @@ -0,0 +1,29 @@ +{ + "app": { + "port": 3003, + "debug": false, + "defaultLanguage": "es", + "senderMail": "noreply@localhost", + "senderName": "" + }, + "mysql": { + "host": "localhost", + "port": 3306, + "user": "reports", + "password": "", + "database": "" + }, + "smtp": { + "host": "localhost", + "port": 465, + "secure": true, + "auth": { + "user": "noreply", + "pass": "" + }, + "tls": { + "rejectUnauthorized": false + }, + "pool": true + } +} \ No newline at end of file diff --git a/services/mailer/application/config/datasources.test.json b/services/mailer/application/config/datasources.test.json new file mode 100644 index 000000000..69943439d --- /dev/null +++ b/services/mailer/application/config/datasources.test.json @@ -0,0 +1,29 @@ +{ + "app": { + "port": 3003, + "debug": false, + "defaultLanguage": "es", + "senderMail": "noreply@localhost", + "senderName": "" + }, + "mysql": { + "host": "localhost", + "port": 3306, + "user": "reports", + "password": "", + "database": "" + }, + "smtp": { + "host": "localhost", + "port": 465, + "secure": true, + "auth": { + "user": "noreply", + "pass": "" + }, + "tls": { + "rejectUnauthorized": false + }, + "pool": true + } +} \ No newline at end of file diff --git a/services/mailer/application/config/mysql.json b/services/mailer/application/config/mysql.json deleted file mode 100644 index ddfd88bed..000000000 --- a/services/mailer/application/config/mysql.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "host": "localhost", - "port": 3306, - "user": "reports", - "password": "", - "database": "" -} \ No newline at end of file diff --git a/services/mailer/application/config/mysql.test.json b/services/mailer/application/config/mysql.test.json deleted file mode 100644 index 5b00829b3..000000000 --- a/services/mailer/application/config/mysql.test.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "host": "localhost", - "port": 3306, - "user": "root", - "password": "", - "database": "vn" -} \ No newline at end of file diff --git a/services/mailer/application/config/smtp.json b/services/mailer/application/config/smtp.json deleted file mode 100644 index 1ffa81024..000000000 --- a/services/mailer/application/config/smtp.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "host": "localhost", - "port": 465, - "secure": true, - "auth": { - "user": "noreply", - "pass": "" - }, - "tls": { - "rejectUnauthorized": false - }, - "pool": true -} \ No newline at end of file diff --git a/services/mailer/application/database.js b/services/mailer/application/database.js index 53ce363f8..4fe4a0ba9 100644 --- a/services/mailer/application/database.js +++ b/services/mailer/application/database.js @@ -1,5 +1,5 @@ var mysql = require('mysql'); -let settings = require('./settings.js'); +let config = require('./config.js'); module.exports = { /** @@ -11,12 +11,12 @@ module.exports = { * Start database pool */ init: function() { - this.pool = mysql.createPool(settings.mysql()); + this.pool = mysql.createPool(config.mysql); this.pool.getConnection(function(error, connection) { if (error) { throw new Error('Can\'t connect to database: ' + error.code); - } else if (settings.app().debug) { + } else if (config.app.debug) { console.log('Database connection stablished'); } }); @@ -27,7 +27,7 @@ module.exports = { */ testEmail: function() { this.pool.query('SELECT fakeEmail as email FROM vn.config', function(error, qryRs) { - settings.testEmail = qryRs[0].email; + config.smtp.testEmail = qryRs[0].email; }); } }; diff --git a/services/mailer/application/locale.js b/services/mailer/application/locale.js index 496d48bb8..f9737fea3 100644 --- a/services/mailer/application/locale.js +++ b/services/mailer/application/locale.js @@ -1,5 +1,5 @@ var fs = require('fs'); -var settings = require('./settings.js'); +var config = require('./config.js'); var path = require('path'); module.exports = { @@ -11,7 +11,7 @@ module.exports = { */ load: function(template, countryCode, cb) { var localeFile = path.join(__dirname, 'template', `${template}`, 'locale', `${countryCode}.json`); - var defaultLocaleFile = path.join(__dirname, 'template', `${template}`, 'locale', `${settings.app().defaultLanguage}.json`); + var defaultLocaleFile = path.join(__dirname, 'template', `${template}`, 'locale', `${config.app.defaultLanguage}.json`); fs.stat(localeFile, (error, stats) => { if (error) { diff --git a/services/mailer/application/mail.js b/services/mailer/application/mail.js index 4807157a9..3980e5128 100644 --- a/services/mailer/application/mail.js +++ b/services/mailer/application/mail.js @@ -1,5 +1,5 @@ var nodemailer = require('nodemailer'); -var settings = require('./settings.js'); +var config = require('./config.js'); var template = require('./template.js'); var database = require('./database.js'); @@ -9,15 +9,15 @@ var database = require('./database.js'); module.exports = { transporter: null, /** - * Load mail settings. + * Load mail config. */ init: function() { - this.transporter = nodemailer.createTransport(settings.smtp()); + this.transporter = nodemailer.createTransport(config.smtp); this.transporter.verify(function(error, success) { if (error) { throw new Error(error); - } else if (settings.app().debug) { + } else if (config.app.debug) { console.log('SMTP connection stablished'); } }); @@ -34,15 +34,15 @@ module.exports = { */ send: function(recipient, subject, body, attachments, params, cb) { let mailOptions = { - from: '"' + settings.app().senderName + '" <' + settings.app().senderMail + '>', + from: '"' + config.app.senderName + '" <' + config.app.senderMail + '>', to: recipient, subject: subject, html: body, attachments }; - if (settings.app().debug) { - mailOptions.to = settings.testEmail; + if (config.app.debug) { + mailOptions.to = config.smtp.testEmail; } this.transporter.sendMail(mailOptions, (error, info) => { @@ -53,7 +53,7 @@ module.exports = { if (error) return cb(new Error('Email not sent: ' + error)); - if (settings.app().debug) + if (config.app.debug) console.log('Mail sent ' + info.messageId + ' [' + info.response + ']'); cb(); @@ -70,10 +70,16 @@ module.exports = { * @param {Object} cb - Callback */ sendWithTemplate: function(tplName, params, cb) { - template.get(tplName, params, false, (error, result) => { + template.get(tplName, params, (error, result) => { if (error) return cb(error); + // Custom attachments + if (params.attachments) + params.attachments.forEach(function(attachment) { + result.attachments.push(attachment); + }); + this.send(result.recipient, result.subject, result.body, result.attachments, params, error => { if (error) return cb(error); @@ -96,10 +102,10 @@ module.exports = { log: function(senderId, recipientId, sender, subject, body, plainTextBody, status) { let qry = `INSERT INTO mail(senderFk, recipientFk, sender, replyTo, subject, body, plainTextBody, sent, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`; - let qryParams = [senderId, recipientId, sender, settings.app().senderMail, subject, body, plainTextBody, 1, status]; + let qryParams = [senderId, recipientId, sender, config.app.senderMail, subject, body, plainTextBody, 1, status]; database.pool.query(qry, qryParams, function(error, result) { - if (settings.app().debug && error) + if (config.app.debug && error) console.log('Mail log: ' + error); }); } diff --git a/services/mailer/application/route/manuscript.js b/services/mailer/application/route/manuscript.js deleted file mode 100644 index b8c54d43b..000000000 --- a/services/mailer/application/route/manuscript.js +++ /dev/null @@ -1,85 +0,0 @@ -var express = require('express'); -var router = new express.Router(); -var mail = require('../mail.js'); -var template = require('../template.js'); -var httpRequest = require('request'); - -// Payment method changes -router.post('/payment-update/:clientId', function(request, response, next) { - mail.sendWithTemplate('payment-update', {recipient: request.params.clientId}, error => { - if (error) - return response.status(400).json({message: error.message}); - - return response.json(); - }); -}); - -// Printer setup -router.post('/printer-setup/:clientId', function(request, response, next) { - mail.sendWithTemplate('printer-setup', {recipient: request.params.clientId}, error => { - if (error) - return response.status(400).json({message: error.message}); - - return response.json(); - }); -}); - -// Printer setup preview -router.get('/printer-setup/:clientId', function(request, response, next) { - template.get('printer-setup', {recipient: request.params.clientId}, true, (error, result) => { - if (error) - return response.status(400).json({message: error.message}); - - response.send(result.body); - }); -}); - -// Client welcome -router.post('/client-welcome/:clientId', function(request, response, next) { - mail.sendWithTemplate('client-welcome', {recipient: request.params.clientId}, error => { - if (error) - return response.status(400).json({message: error.message}); - - return response.json(); - }); -}); - -// Client welcome preview -router.get('/client-welcome/:clientId', function(request, response, next) { - template.get('client-welcome', {recipient: request.params.clientId}, true, (error, result) => { - if (error) - return response.status(400).json({message: error.message}); - - response.send(result.body); - }); -}); - -// Sepa core -/* router.post('/sepa-core/:clientId', function(request, response, next) { - var options = { - url: 'http://localhost:3008/manuscript/sepa-core/7422', - method: 'POST', - headers: { - 'authorization': request.headers.authorization - } - } - - let httpStream = httpRequest(options, function(error, httpResponse, body) { - if (error) - return response.status(400).json({message: httpResponse.message}); - }); - - if (httpStream) - mail.send('joan@verdnatura.es', 'Correu de prova', 'test message', [{filename: 'test.pdf', content: httpStream}], function(error, result) { - if (error) - return response.status(400).json({message: error.message}); - }); - /* mail.sendWithTemplate('sepa-core', {recipient: request.params.clientId}, error => { - if (error) - return response.status(400).json({message: error.message}); - - return response.json(); - }); -}); */ - -module.exports = router; diff --git a/services/mailer/application/route/notification.js b/services/mailer/application/route/notification.js index d33f6f07b..bd7a81f11 100644 --- a/services/mailer/application/route/notification.js +++ b/services/mailer/application/route/notification.js @@ -1,8 +1,92 @@ var express = require('express'); var router = new express.Router(); +var config = require('../config.js'); var mail = require('../mail.js'); -var database = require('../database.js'); -var settings = require('../settings.js'); +var template = require('../template.js'); +var httpRequest = require('request'); + + +// Printer setup +router.post('/printer-setup/:clientId', function(request, response) { + mail.sendWithTemplate('printer-setup', {clientId: request.params.clientId}, error => { + if (error) + return response.status(400).json({message: error.message}); + + return response.json(); + }); +}); + +// Printer setup preview +router.get('/printer-setup/:clientId', function(request, response) { + template.get('printer-setup', {clientId: request.params.clientId, isPreview: true}, (error, result) => { + if (error) + return response.status(400).json({message: error.message}); + + response.send(result.body); + }); +}); + +// Client welcome +router.post('/client-welcome/:clientId', function(request, response) { + mail.sendWithTemplate('client-welcome', {clientId: request.params.clientId}, error => { + if (error) + return response.status(400).json({message: error.message}); + + return response.json(); + }); +}); + +// Client welcome preview +router.get('/client-welcome/:clientId', function(request, response) { + template.get('client-welcome', {clientId: request.params.clientId, isPreview: true}, (error, result) => { + if (error) + return response.status(400).json({message: error.message}); + + response.send(result.body); + }); +}); + +// Client SEPA CORE +router.post('/sepa-core/:clientId', function(request, response) { + let path = `${request.proxyHost}/print/manuscript/sepa-core/${request.params.clientId}`; + let options = { + url: path, + method: 'GET', + headers: { + 'Authorization': request.headers.authorization + } + } + + let httpStream = httpRequest(options, function(error, httpResponse, body) { + if (error || httpResponse.statusCode != 200) + return response.status(400).json({message: error.message}); + }); + + if (httpStream) + mail.sendWithTemplate('sepa-core', { + clientId: request.params.clientId, + attachments: [{filename: 'sepa-core.pdf', content: httpStream}] + }, error => { + if (error) + return response.status(400).json({message: error.message}); + + return response.json(); + }); +}); + +// Client SEPA CORE preview +router.get('/sepa-core/:clientId', function(request, response) { + template.get('sepa-core', { + clientId: request.params.clientId, + token: request.user.token, + isPreview: true + }, (error, result) => { + if (error) + return response.status(400).json({message: error.message}); + + response.send(result.body); + }); +}); // Single user notification /* router.post('/:recipient/noticeUserSend', function(request, response) { @@ -64,6 +148,16 @@ var settings = require('../settings.js'); }); }); */ +// Payment method changes +router.post('/payment-update/:clientId', function(request, response) { + mail.sendWithTemplate('payment-update', {clientId: request.params.clientId}, error => { + if (error) + return response.status(400).json({message: error.message}); + + return response.json(); + }); +}); + // Send notification to alias creditInsurance on client deactivate router.post('/client-deactivate/:clientId', function(request, response) { var params = { diff --git a/services/mailer/application/router.js b/services/mailer/application/router.js index b51b8a195..ed5794bbe 100644 --- a/services/mailer/application/router.js +++ b/services/mailer/application/router.js @@ -1,15 +1,11 @@ var express = require('express'); var router = new express.Router(); -var settings = require('./settings.js'); // Mailer default page router.get('/', function(request, response) { response.json({}); }); -// Manuscripts -router.use('/manuscript', require('./route/manuscript.js')); - // Notifications router.use('/notification', require('./route/notification.js')); diff --git a/services/mailer/application/settings.js b/services/mailer/application/settings.js deleted file mode 100644 index 3858cf719..000000000 --- a/services/mailer/application/settings.js +++ /dev/null @@ -1,45 +0,0 @@ -var path = require('path'); -/** - * Módulo de configuración - */ -module.exports = { - /** - * Obtiene la configuración en función del entorno en el que se está - * ejecutando la aplicación. - * @param {String} name Nombre del fichero - * @return {Object} Objeto de configuración - */ - getConfig: function(name) { - let env = process.env.NODE_ENV; - - if (!env) - env = 'development'; - - return require(path.join(__dirname, 'config', `${name}.${env}.json`)); - }, - /** - * Configuración de la aplicación - * @return {Object} Objeto de configuración app - */ - app: function() { - return this.getConfig('app'); - }, - /** - * Configuración de smtp - * @return {Object} Objeto de configuración smtp - */ - smtp: function() { - return this.getConfig('smtp'); - }, - /** - * Configuración de mysql - * @return {Object} Objeto de configuración MySQL - */ - mysql: function() { - return this.getConfig('mysql'); - }, - - testEmail: function() { - return this.getConfig('app').testEmail; - } -}; diff --git a/services/mailer/application/template.js b/services/mailer/application/template.js index 382b7202d..d2c13f222 100644 --- a/services/mailer/application/template.js +++ b/services/mailer/application/template.js @@ -1,8 +1,8 @@ var fs = require('fs'); var mustache = require('mustache'); var locale = require('./locale.js'); -var path = require('path'); var inlineCss = require('inline-css'); +var path = require('path'); module.exports = { /** @@ -12,10 +12,10 @@ module.exports = { * @param {Object} params - Params * @param {Object} cb - Callback */ - get: function(template, params, isPreview, cb) { + get: function(template, params, cb) { var templatePath = path.join(__dirname, 'template', `${template}`, `index.html`); var classPath = path.join(__dirname, 'template', `${template}`, `${template}.js`); - var stylePath = path.join(__dirname, '../static', 'css', 'style.css'); + var stylePath = path.join(__dirname, 'template', `${template}`, 'static', 'css', 'style.css'); fs.stat(templatePath, (error, stat) => { if (error) @@ -24,14 +24,26 @@ module.exports = { let TemplateClass = require(classPath); let instance = new TemplateClass(); - let getRenderedStyles = body => { + let getRenderedStyles = (error, body) => { + if (error) + return cb(error); + this.renderStyles(stylePath, body, (error, body) => { + if (error) + return cb(error); + + // Check if has a subject param params.subject = params.subject || instance.subject; - if (params.subject == undefined) - params.subject = body.match(new RegExp('(.*?)', 'i'))[1]; + if (params.subject == undefined) { + // Try to find a subject from Html source + let title = body.match(new RegExp('(.*?)', 'i')); - this.getAttachments(template, body, isPreview, (error, result) => { + if (title) + params.subject = title[1]; + } + + this.getAttachments(template, body, params.isPreview, (error, result) => { if (error) return cb(error); @@ -41,7 +53,7 @@ module.exports = { }; let getDataCb = () => { - this.render(templatePath, instance, (error, result) => getRenderedStyles(result)); + this.render(templatePath, instance, (error, result) => getRenderedStyles(error, result)); }; instance.getData(params, (error, result) => { @@ -66,9 +78,54 @@ module.exports = { * @param {Object} cb - Callback */ render: function(path, data, cb) { - fs.readFile(path, 'utf8', function(error, body) { - mustache.parse(body); - cb(null, mustache.render(body, data)); + fs.readFile(path, 'utf8', (error, body) => { + // Find matching sub-templates + let regexp = new RegExp(/\{\{\$\.(.*?)\}\}/, 'ig'); + let subTpl = body.match(regexp); + + if (!subTpl) { + mustache.parse(body); + return cb(null, mustache.render(body, data)); + } + + let parentBody = body; + this.renderSub(parentBody, subTpl, data, regexp, (error, body) => { + if (error) + return cb(error); + + mustache.parse(body); + cb(null, mustache.render(body, data)); + }); + }); + }, + +/** + * Render sub-template + * @param {String} body - Raw body + * @param {Object} subTpl - Sub-template name + * @param {Object} data - Params + * @param {Object} regexp - Regexp + * @param {Object} cb - Callback + */ + renderSub: function(body, subTpl, data, regexp, cb) { + let index = 1; + + subTpl.forEach(keyName => { + subTplName = keyName.replace(regexp, '$1'); + + this.get(subTplName, data, (error, result) => { + if (error) + return cb(error); + + let subTplBody = result.body; + body = body.replace(keyName, subTplBody); + + if (index === subTpl.length) + cb(null, body); + + index++; + }); + }); }, @@ -78,18 +135,25 @@ module.exports = { * @param {String} body - Rendered html * @param {Object} cb - Callback */ - renderStyles: function(path, html, cb) { - fs.stat(path, error => { - if (error) return cb(new Error('Template stylesheet not found')); - fs.readFile(path, 'utf8', (error, css) => { - let style = ''; - let body = style + html; - let options = {url: ' '}; + renderStyles: function(stylePath, html, cb) { + // Common components + let comPath = path.join(__dirname, '../', 'static', 'css', 'component.css'); - inlineCss(body, options) - .then(function(body) { - cb(null, body); - }); + fs.readFile(comPath, 'utf8', (error, comCss) => { + fs.stat(stylePath, error => { + if (error) + return cb(new Error('Template stylesheet not found')); + + fs.readFile(stylePath, 'utf8', (error, css) => { + let style = ''; + let body = style + html; + let options = {url: ' '}; + + inlineCss(body, options) + .then(function(body) { + cb(null, body); + }); + }); }); }); }, @@ -104,6 +168,9 @@ module.exports = { let attachments = []; let tplAttachments = body.match(new RegExp('src="cid:(.*?)"', 'ig')); + if (!tplAttachments) + tplAttachments = {}; + // Template default attachments for (var i = 0; i < tplAttachments.length; i++) { let name = tplAttachments[i].replace('src="cid:', '').replace('"', ''); @@ -125,7 +192,7 @@ module.exports = { fs.stat(attachmentsPath, (error, stats) => { if (error) - return cb(new Error(`Could not load attachments.js from template ${template}`)); + return cb(null, {body: body, attachments: attachments}); let attachObj = require(attachmentsPath); @@ -145,10 +212,10 @@ module.exports = { }, /** - * Check all template attachments - * @param {Object} attachments - Attachments object - * @param {Object} cb - Callback - */ +* Check all template attachments +* @param {Object} attachments - Attachments object +* @param {Object} cb - Callback +*/ checkAttachments: function(attachments, cb) { for (var i = 0; i < attachments.length; i++) { var attachment = attachments[i]; @@ -159,4 +226,4 @@ module.exports = { } cb(); } -}; +}; \ No newline at end of file diff --git a/services/mailer/application/template/client-welcome/client-welcome.js b/services/mailer/application/template/client-welcome/client-welcome.js index 851c9a12e..469e74386 100644 --- a/services/mailer/application/template/client-welcome/client-welcome.js +++ b/services/mailer/application/template/client-welcome/client-welcome.js @@ -5,6 +5,7 @@ var format = require(path.join(__dirname, '../../util/format.js')); module.exports = class ClientWelcome { getData(params, cb) { let query = `SELECT + c.id clientId, CONCAT(w.name, ' ', w.firstName) name, w.phone AS phone, CONCAT(wu.name, '@verdnatura.es') AS email, @@ -17,7 +18,7 @@ module.exports = class ClientWelcome { LEFT JOIN account.user wu ON wu.id = w.userFk JOIN country ct ON ct.id = c.countryFk WHERE c.id = ?`; - database.pool.query(query, [params.recipient], (error, result) => { + database.pool.query(query, [params.clientId], (error, result) => { if (error || result.length == 0) return cb(new Error('No template data found')); diff --git a/services/mailer/application/template/client-welcome/index.html b/services/mailer/application/template/client-welcome/index.html index 661f0986a..a8a711c8e 100644 --- a/services/mailer/application/template/client-welcome/index.html +++ b/services/mailer/application/template/client-welcome/index.html @@ -7,11 +7,9 @@
- - - + + {{$.header}} +
@@ -21,108 +19,47 @@
-

{{_.dear}},

-

{{_.bodyDescription}}

- -

- Sus datos para poder comprar en la web de verdnatura (https://www.verdnatura.es) - o en nuestras aplicaciones para iOS (https://goo.gl/3hC2mG) y Android (https://goo.gl/8obvLc), son: -

- +

{{_.dear}}

+

{{{_.bodyDescription}}}

-

Usuario: {{userName}}
-
Contraseña: ******** (Va a recibir un correo para establecer la contraseña)
+
{{_.user}} {{userName}}
+
{{_.password}} ******** {{_.passwordResetText}}

-

Cómo hacer un pedido

- -

Para realizar un pedido en nuestra web, debe configurarlo indicando:

- +

{{_.sectionHowToBuyTitle}}

+

{{_.sectionHowToBuyDescription}}

    -
  1. Si quiere recibir el pedido (por agencia o por nuestro propio reparto) o si lo prefiere recoger en alguno de nuestros almacenes.
  2. -
  3. La fecha en la que quiera recibir el pedido (se preparará el día anterior).
  4. -
  5. La dirección de entrega o el almacén donde quiera recoger el pedido.
  6. +
  7. {{_.sectionHowToBuyRequeriment1}}
  8. +
  9. {{_.sectionHowToBuyRequeriment2}}
  10. +
  11. {{_.sectionHowToBuyRequeriment3}}
- -

En nuestra web y aplicaciones puedes visualizar el stock disponible de flor cortada, verdes, plantas, complementos y artificial. - Tenga 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.

- -

El reparto se realiza de lunes a sábado según la zona en la que se encuentre. 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.

- -

Cómo pagar

- -

Las formas de pago admitidas en Verdnatura son

- +

{{_.sectionHowToBuyStock}}

+

{{_.sectionHowToBuyDelivery}}

+ +

{{_.sectionHowToPayTitle}}

+

{{_.sectionHowToPayDescription}}

    -
  • Con tarjeta a través de nuestra plataforma web (al confirmar el pedido).
  • -
  • Mediante giro bancario mensual, modalidad que hay que solicitar y tramitar.
  • +
  • {{{_.sectionHowToPayOption1}}}
  • +
  • {{{_.sectionHowToPayOption2}}}
-

Cosas a tener en cuenta

- -

Verdnatura vende EXCLUSIVAMENTE a profesionales, por lo que debe remitirnos el Modelo 036 ó 037, - para comprobar que está dado/a de alta en el epígrafe correspondiente al comercio de flores.

- -

POLÍTICA DE RECLAMACIONES

- -

Verdnatura aceptará las reclamaciones que se realicen dentro de los dos días naturales - siguientes a la recepción del pedido (incluyendo el mismo día de la recepción). Pasado este plazo no se aceptará ninguna reclamación.

- -

Cualquier duda que le surja, no dude en consultarla, ¡estamos para atenderle!

+

{{_.sectionToConsiderTitle}}

+

{{_.sectionToConsiderDescription}}

+

{{_.sectionClaimsPolicyTitle}}

+

{{_.sectionClaimsPolicyDescription}}

+

{{{_.doubtsText}}}

{{{salesPersonName}}} {{{salesPersonPhone}}} {{{salesPersonEmail}}}

- -
- - - - - - - - - -
-

{{_.fiscalAddress}}

-

{{_.privacy}}

-

{{_.privacyLaw}}

-
- + + {{$.footer}} +
diff --git a/services/mailer/application/template/client-welcome/locale/es.json b/services/mailer/application/template/client-welcome/locale/es.json index c99fe0c8e..b1149d62b 100644 --- a/services/mailer/application/template/client-welcome/locale/es.json +++ b/services/mailer/application/template/client-welcome/locale/es.json @@ -1,14 +1,28 @@ { "subject": "¡Le damos la bienvenida!", "title": "¡LE DAMOS LA BIENVENIDA!", - "dear": "Estimado cliente", - "bodyDescription": "Siga las intrucciones especificadas en este correo para llevar a cabo la instalación de la impresora.", + "dear": "Estimado cliente,", + "bodyDescription": "Sus datos para poder comprar en la web de verdnatura (https://www.verdnatura.es) o en nuestras aplicaciones para iOS y Android (Ver tutorial de uso), son:", + "user": "Usuario:", + "password": "Contraseña:", + "passwordResetText": "(Va a recibir un correo para establecer la contraseña)", + "sectionHowToBuyTitle": "Cómo hacer un pedido", + "sectionHowToBuyDescription": "Para realizar un pedido en nuestra web, debe configurarlo indicando:", + "sectionHowToBuyRequeriment1": "Si quiere recibir el pedido (por agencia o por nuestro propio reparto) o si lo prefiere recoger en alguno de nuestros almacenes.", + "sectionHowToBuyRequeriment2": "La fecha en la que quiera recibir el pedido (se preparará el día anterior).", + "sectionHowToBuyRequeriment3": "La dirección de entrega o el almacén donde quiera recoger el pedido.", + "sectionHowToBuyStock": "En nuestra web y aplicaciones puedes visualizar el stock disponible de flor cortada, verdes, plantas, complementos y artificial. Tenga 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.", + "sectionHowToBuyDelivery": "El reparto se realiza de lunes a sábado según la zona en la que se encuentre. 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.", + "sectionHowToPayTitle": "Cómo pagar", + "sectionHowToPayDescription": "Las formas de pago admitidas en Verdnatura son:", + "sectionHowToPayOption1": "Con tarjeta a través de nuestra plataforma web (al confirmar el pedido).", + "sectionHowToPayOption2": "Mediante giro bancario mensual, modalidad que hay que solicitar y tramitar.", + "sectionToConsiderTitle": "Cosas a tener en cuenta", + "sectionToConsiderDescription": "Verdnatura vende EXCLUSIVAMENTE a profesionales, por lo que debe remitirnos el Modelo 036 ó 037, para comprobar que está dado/a de alta en el epígrafe correspondiente al comercio de flores.", + "sectionClaimsPolicyTitle": "POLÍTICA DE RECLAMACIONES", + "sectionClaimsPolicyDescription": "Verdnatura aceptará las reclamaciones que se realicen dentro de los dos días naturales siguientes a la recepción del pedido (incluyendo el mismo día de la recepción). Pasado este plazo no se aceptará ninguna reclamación.", + "doubtsText": "Cualquier duda que le surja, no dude en consultarla, ¡estamos para atenderle!", "salesPersonNameText": "Soy tu comercial y mi nombre es", "salesPersonPhoneText": "Teléfono y whatsapp", - "salesPersonEmailText": "Dirección de e-mail", - "actionButton": "Visita nuestra Web", - "infoButton": "Ayúdanos a mejorar", - "fiscalAddress": "VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla _ www.verdnatura.es _ clientes@verdnatura.es", - "privacy": "- AVISO - Este mensaje es privado y confidencial, y debe ser utilizado exclusivamente por la persona destinataria del mismo. Si usted ha recibido este mensaje por error, le rogamos lo comunique al remitente y borre 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.", - "privacyLaw": "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." + "salesPersonEmailText": "Dirección de e-mail" } \ No newline at end of file diff --git a/services/mailer/application/template/client-welcome/static/css/style.css b/services/mailer/application/template/client-welcome/static/css/style.css new file mode 100644 index 000000000..e69de29bb diff --git a/services/mailer/application/template/footer/footer.js b/services/mailer/application/template/footer/footer.js new file mode 100644 index 000000000..3b4020bf4 --- /dev/null +++ b/services/mailer/application/template/footer/footer.js @@ -0,0 +1,20 @@ +var path = require('path'); +var database = require(path.join(__dirname, '../../database.js')); +var format = require(path.join(__dirname, '../../util/format.js')); + +module.exports = class Footer { + getData(params, cb) { + let query = `SELECT + socialName + FROM client c + JOIN country ct ON ct.id = c.countryFk + WHERE c.id = ?`; + database.pool.query(query, [params.clientId], (error, result) => { + if (error || result.length == 0) + return cb(new Error('No template data found')); + + Object.assign(this, result[0]); + cb(); + }); + } +}; diff --git a/services/mailer/application/template/footer/index.html b/services/mailer/application/template/footer/index.html new file mode 100644 index 000000000..e52f7a695 --- /dev/null +++ b/services/mailer/application/template/footer/index.html @@ -0,0 +1,42 @@ + + + + + + + + + +
+

{{_.fiscalAddress}}

+

{{_.privacy}}

+

{{_.privacyLaw}}

+
+ \ No newline at end of file diff --git a/services/mailer/application/template/footer/locale/es.json b/services/mailer/application/template/footer/locale/es.json new file mode 100644 index 000000000..1ad532d34 --- /dev/null +++ b/services/mailer/application/template/footer/locale/es.json @@ -0,0 +1,7 @@ +{ + "actionButton": "Visita nuestra Web", + "infoButton": "Ayúdanos a mejorar", + "fiscalAddress": "VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla · www.verdnatura.es · clientes@verdnatura.es", + "privacy": "- AVISO - Este mensaje es privado y confidencial, y debe ser utilizado exclusivamente por la persona destinataria del mismo. Si usted ha recibido este mensaje por error, le rogamos lo comunique al remitente y borre 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.", + "privacyLaw": "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." +} \ No newline at end of file diff --git a/services/mailer/static/css/style.css b/services/mailer/application/template/footer/static/css/style.css similarity index 58% rename from services/mailer/static/css/style.css rename to services/mailer/application/template/footer/static/css/style.css index 1243a091c..f35364e8c 100644 --- a/services/mailer/static/css/style.css +++ b/services/mailer/application/template/footer/static/css/style.css @@ -1,41 +1,3 @@ -img { - margin: 0 -} - -.wrapper { - background-color: #EEE -} - -.container { - font-family: arial, sans-serif; - max-width: 600px; - min-width: 320px; - font-size: 16px; - margin: 0 auto; - color: #555 -} - -.banner img { - width: 100% -} - -.title { - background-color: #95d831; - text-align: center; - padding: 35px 0 -} - -.title h1 { - font-size: 32px; - color: #333; - margin: 0 -} - -.body { - background-color:#FFF; - padding: 20px -} - .buttons { background-color: #FFF; text-align: center; diff --git a/services/mailer/application/template/footer/static/image/action.png b/services/mailer/application/template/footer/static/image/action.png new file mode 100644 index 000000000..2cd16c453 Binary files /dev/null and b/services/mailer/application/template/footer/static/image/action.png differ diff --git a/services/mailer/application/template/footer/static/image/facebook.png b/services/mailer/application/template/footer/static/image/facebook.png new file mode 100644 index 000000000..7ab54c538 Binary files /dev/null and b/services/mailer/application/template/footer/static/image/facebook.png differ diff --git a/services/mailer/application/template/footer/static/image/header.png b/services/mailer/application/template/footer/static/image/header.png new file mode 100644 index 000000000..3c063ae44 Binary files /dev/null and b/services/mailer/application/template/footer/static/image/header.png differ diff --git a/services/mailer/application/template/footer/static/image/info.png b/services/mailer/application/template/footer/static/image/info.png new file mode 100644 index 000000000..fb75cbc4e Binary files /dev/null and b/services/mailer/application/template/footer/static/image/info.png differ diff --git a/services/mailer/application/template/footer/static/image/instagram.png b/services/mailer/application/template/footer/static/image/instagram.png new file mode 100644 index 000000000..66550c4a5 Binary files /dev/null and b/services/mailer/application/template/footer/static/image/instagram.png differ diff --git a/services/mailer/application/template/footer/static/image/linkedin.png b/services/mailer/application/template/footer/static/image/linkedin.png new file mode 100644 index 000000000..0d191e5ae Binary files /dev/null and b/services/mailer/application/template/footer/static/image/linkedin.png differ diff --git a/services/mailer/application/template/footer/static/image/logo.png b/services/mailer/application/template/footer/static/image/logo.png new file mode 100644 index 000000000..55e26fec6 Binary files /dev/null and b/services/mailer/application/template/footer/static/image/logo.png differ diff --git a/services/mailer/application/template/footer/static/image/logo.svg b/services/mailer/application/template/footer/static/image/logo.svg new file mode 100644 index 000000000..51baf46d3 --- /dev/null +++ b/services/mailer/application/template/footer/static/image/logo.svg @@ -0,0 +1,48 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/services/mailer/application/template/footer/static/image/pinterest.png b/services/mailer/application/template/footer/static/image/pinterest.png new file mode 100644 index 000000000..4d7b28ef8 Binary files /dev/null and b/services/mailer/application/template/footer/static/image/pinterest.png differ diff --git a/services/mailer/application/template/footer/static/image/twitter.png b/services/mailer/application/template/footer/static/image/twitter.png new file mode 100644 index 000000000..c4a8ab0c1 Binary files /dev/null and b/services/mailer/application/template/footer/static/image/twitter.png differ diff --git a/services/mailer/application/template/footer/static/image/youtube.png b/services/mailer/application/template/footer/static/image/youtube.png new file mode 100644 index 000000000..11871deb5 Binary files /dev/null and b/services/mailer/application/template/footer/static/image/youtube.png differ diff --git a/services/mailer/application/template/header/header.js b/services/mailer/application/template/header/header.js new file mode 100644 index 000000000..82c78e003 --- /dev/null +++ b/services/mailer/application/template/header/header.js @@ -0,0 +1,20 @@ +var path = require('path'); +var database = require(path.join(__dirname, '../../database.js')); +var format = require(path.join(__dirname, '../../util/format.js')); + +module.exports = class Header { + getData(params, cb) { + let query = `SELECT + c.name AS clientName + FROM client c + JOIN country ct ON ct.id = c.countryFk + WHERE c.id = ?`; + database.pool.query(query, [params.clientId], (error, result) => { + if (error || result.length == 0) + return cb(new Error('No template data found')); + + Object.assign(this, result[0]); + cb(); + }); + } +}; diff --git a/services/mailer/application/template/header/index.html b/services/mailer/application/template/header/index.html new file mode 100644 index 000000000..c5234bbc8 --- /dev/null +++ b/services/mailer/application/template/header/index.html @@ -0,0 +1,3 @@ +
+ VerdNatura +
diff --git a/services/mailer/application/template/header/locale/es.json b/services/mailer/application/template/header/locale/es.json new file mode 100644 index 000000000..7a73a41bf --- /dev/null +++ b/services/mailer/application/template/header/locale/es.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/services/mailer/application/template/header/static/css/style.css b/services/mailer/application/template/header/static/css/style.css new file mode 100644 index 000000000..4ea8ee36d --- /dev/null +++ b/services/mailer/application/template/header/static/css/style.css @@ -0,0 +1,3 @@ +.banner img { + width: 100% +} \ No newline at end of file diff --git a/services/mailer/application/template/header/static/image/action.png b/services/mailer/application/template/header/static/image/action.png new file mode 100644 index 000000000..2cd16c453 Binary files /dev/null and b/services/mailer/application/template/header/static/image/action.png differ diff --git a/services/mailer/application/template/header/static/image/facebook.png b/services/mailer/application/template/header/static/image/facebook.png new file mode 100644 index 000000000..7ab54c538 Binary files /dev/null and b/services/mailer/application/template/header/static/image/facebook.png differ diff --git a/services/mailer/application/template/header/static/image/header.png b/services/mailer/application/template/header/static/image/header.png new file mode 100644 index 000000000..3c063ae44 Binary files /dev/null and b/services/mailer/application/template/header/static/image/header.png differ diff --git a/services/mailer/application/template/header/static/image/info.png b/services/mailer/application/template/header/static/image/info.png new file mode 100644 index 000000000..fb75cbc4e Binary files /dev/null and b/services/mailer/application/template/header/static/image/info.png differ diff --git a/services/mailer/application/template/header/static/image/instagram.png b/services/mailer/application/template/header/static/image/instagram.png new file mode 100644 index 000000000..66550c4a5 Binary files /dev/null and b/services/mailer/application/template/header/static/image/instagram.png differ diff --git a/services/mailer/application/template/header/static/image/linkedin.png b/services/mailer/application/template/header/static/image/linkedin.png new file mode 100644 index 000000000..0d191e5ae Binary files /dev/null and b/services/mailer/application/template/header/static/image/linkedin.png differ diff --git a/services/mailer/application/template/header/static/image/logo.png b/services/mailer/application/template/header/static/image/logo.png new file mode 100644 index 000000000..55e26fec6 Binary files /dev/null and b/services/mailer/application/template/header/static/image/logo.png differ diff --git a/services/mailer/application/template/header/static/image/logo.svg b/services/mailer/application/template/header/static/image/logo.svg new file mode 100644 index 000000000..51baf46d3 --- /dev/null +++ b/services/mailer/application/template/header/static/image/logo.svg @@ -0,0 +1,48 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/services/mailer/application/template/header/static/image/pinterest.png b/services/mailer/application/template/header/static/image/pinterest.png new file mode 100644 index 000000000..4d7b28ef8 Binary files /dev/null and b/services/mailer/application/template/header/static/image/pinterest.png differ diff --git a/services/mailer/application/template/header/static/image/twitter.png b/services/mailer/application/template/header/static/image/twitter.png new file mode 100644 index 000000000..c4a8ab0c1 Binary files /dev/null and b/services/mailer/application/template/header/static/image/twitter.png differ diff --git a/services/mailer/application/template/header/static/image/youtube.png b/services/mailer/application/template/header/static/image/youtube.png new file mode 100644 index 000000000..11871deb5 Binary files /dev/null and b/services/mailer/application/template/header/static/image/youtube.png differ diff --git a/services/mailer/application/template/notification-alias/index.html b/services/mailer/application/template/notification-alias/index.html index ab00f8ef6..6e79e1278 100644 --- a/services/mailer/application/template/notification-alias/index.html +++ b/services/mailer/application/template/notification-alias/index.html @@ -5,70 +5,29 @@ -
- -
- VerdNatura -
- +
+
+ + {{$.header}} + - -
-

{{_.title}}

-
- + +
+

{{_.title}}

+
+ - -
-

{{_.hello}}, #{{alias}}

-

{{message}}

-

- + +
+

{{_.hello}} #{{alias}}

+

{{message}}

+
+ - -
- - - - - - - -
-

{{_.fiscalAddress}}

-

{{_.privacy}}

-

{{_.privacyLaw}}

-
- -
\ No newline at end of file diff --git a/services/mailer/application/template/notification-alias/locale/es.json b/services/mailer/application/template/notification-alias/locale/es.json index 0deeb1604..6e39b7f63 100644 --- a/services/mailer/application/template/notification-alias/locale/es.json +++ b/services/mailer/application/template/notification-alias/locale/es.json @@ -1,16 +1,11 @@ { "subject": "Has recibido una nueva notificación", "title": "Nueva notificación", - "hello": "Hola", + "hello": "Hola,", "notificationCode": { "clientDeactivate": { "subject": "Gestionar baja de contrato", "message": "El cliente con id %clientId% está clasificado, por favor, gestione la baja del contrato primero." } - }, - "actionButton": "Visita nuestra Web", - "infoButton": "Ayúdanos a mejorar", - "fiscalAddress": "VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla _ www.verdnatura.es _ clientes@verdnatura.es", - "privacy": "- AVISO - Este mensaje es privado y confidencial, y debe ser utilizado exclusivamente por la persona destinataria del mismo. Si usted ha recibido este mensaje por error, le rogamos lo comunique al remitente y borre 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.", - "privacyLaw": "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." + } } \ No newline at end of file diff --git a/services/mailer/application/template/notification-alias/static/css/style.css b/services/mailer/application/template/notification-alias/static/css/style.css new file mode 100644 index 000000000..e69de29bb diff --git a/services/mailer/application/template/payment-update/index.html b/services/mailer/application/template/payment-update/index.html index caec77e90..78d6612c4 100644 --- a/services/mailer/application/template/payment-update/index.html +++ b/services/mailer/application/template/payment-update/index.html @@ -7,11 +7,9 @@
- - - + + {{$.header}} +
@@ -21,59 +19,20 @@
-

{{_.dear}},

-

{{_.bodyDescription}}

-

-

{{_.paymentMethod}}: {{payMethodName}}
+

{{_.dear}}

+

{{_.bodyDescription}}

+

+

{{_.paymentMethod}} {{payMethodName}}
{{{paymentDay}}}

-

{{paymentAdvice}}

-

{{_.notifyError}}

+

{{paymentAdvice}}

+

{{_.notifyError}}

- - - - - - - - - -
-

{{_.fiscalAddress}}

-

{{_.privacy}}

-

{{_.privacyLaw}}

-
- + + {{$.footer}} +
diff --git a/services/mailer/application/template/payment-update/locale/es.json b/services/mailer/application/template/payment-update/locale/es.json index 144cdc346..1216dcd99 100644 --- a/services/mailer/application/template/payment-update/locale/es.json +++ b/services/mailer/application/template/payment-update/locale/es.json @@ -1,18 +1,13 @@ { "subject": "Cambios en las condiciones de pago", "title": "Cambio en las condiciones", - "dear": "Estimado cliente", + "dear": "Estimado cliente,", "bodyDescription": "Le informamos que han cambiado las condiciones de pago de su cuenta. A continuación le indicamos las nuevas condiciones:", - "paymentMethod": "Método de pago", - "paymentDay": "Día de pago", + "paymentMethod": "Método de pago:", + "paymentDay": "Día de pago:", "everyMonth": "de cada mes", "cardPaymentAdvice": "Su modo de pago actual implica que deberá abonar el importe de los pedidos realizados en el mismo día para que se puedan enviar.", "accountPaymentAdviceBefore": "Su modo de pago actual implica que se le pasará un cargo a la cuenta", "accountPaymentAdviceAfter": "por el importe pendiente, al vencimiento establecido en las condiciones.", - "notifyError": "En el caso de detectar algún error en los datos indicados o para cualquier aclaración, debe dirigirse a su comercial.", - "actionButton": "Visita nuestra Web", - "infoButton": "Ayúdanos a mejorar", - "fiscalAddress": "VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla _ www.verdnatura.es _ clientes@verdnatura.es", - "privacy": "- AVISO - Este mensaje es privado y confidencial, y debe ser utilizado exclusivamente por la persona destinataria del mismo. Si usted ha recibido este mensaje por error, le rogamos lo comunique al remitente y borre 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.", - "privacyLaw": "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." + "notifyError": "En el caso de detectar algún error en los datos indicados o para cualquier aclaración, debe dirigirse a su comercial." } \ No newline at end of file diff --git a/services/mailer/application/template/payment-update/payment-update.js b/services/mailer/application/template/payment-update/payment-update.js index d126dc83a..187d21b73 100644 --- a/services/mailer/application/template/payment-update/payment-update.js +++ b/services/mailer/application/template/payment-update/payment-update.js @@ -5,6 +5,7 @@ var format = require(path.join(__dirname, '../../util/format.js')); module.exports = class PaymentUpdate { getData(params, cb) { let query = `SELECT + c.id clientId, pm.id payMethodFk, pm.name payMethodName, c.dueDay, @@ -15,7 +16,7 @@ module.exports = class PaymentUpdate { JOIN payMethod pm ON pm.id = c.paymentMethodFk JOIN country ct ON ct.id = c.countryFk WHERE c.id = ?`; - database.pool.query(query, [params.recipient], (error, result) => { + database.pool.query(query, [params.clientId], (error, result) => { if (error || result.length == 0) return cb(new Error('No template data found')); @@ -26,7 +27,7 @@ module.exports = class PaymentUpdate { get paymentDay() { if (this.payMethodFk != 5) - return `
${this._.paymentDay}: ${this.dueDay} ${this._.everyMonth}
`; + return `
${this._.paymentDay} ${this.dueDay} ${this._.everyMonth}
`; } get paymentAdvice() { diff --git a/services/mailer/application/template/payment-update/static/css/style.css b/services/mailer/application/template/payment-update/static/css/style.css new file mode 100644 index 000000000..e69de29bb diff --git a/services/mailer/application/template/printer-setup/index.html b/services/mailer/application/template/printer-setup/index.html index 3f6c8910d..ee021281c 100644 --- a/services/mailer/application/template/printer-setup/index.html +++ b/services/mailer/application/template/printer-setup/index.html @@ -7,11 +7,9 @@
- - - + + {{$.header}} +
@@ -21,14 +19,14 @@
-

{{_.dear}},

-

{{_.bodyDescription}}

-

{{{_.followGuide}}}

+

{{_.dear}}

+

{{_.bodyDescription}}

+

{{{_.followGuide}}}

{{{_.downloadFrom}}}

-

{{_.sectionQLabelTitle}}

+

{{_.sectionQLabelTitle}}

-

{{_.sectionQLabelDescription}}

+

{{_.sectionQLabelDescription}}

  1. {{_.sectionQLabelStep1}}
  2. @@ -48,8 +46,9 @@
  3. {{_.sectionQLabelStep15}}
-

{{_.sectionHelpTitle}}

-

{{_.sectionHelpDescription}}

+

{{_.sectionHelpTitle}}

+ +

{{_.sectionHelpDescription}}

{{{_.sectionHelpDownloadRemoteSupport}}}

@@ -60,48 +59,9 @@

- - - - - - - - - -
-

{{_.fiscalAddress}}

-

{{_.privacy}}

-

{{_.privacyLaw}}

-
- + + {{$.footer}} +
diff --git a/services/mailer/application/template/printer-setup/locale/es.json b/services/mailer/application/template/printer-setup/locale/es.json index 41a831524..7d3da6646 100644 --- a/services/mailer/application/template/printer-setup/locale/es.json +++ b/services/mailer/application/template/printer-setup/locale/es.json @@ -1,7 +1,7 @@ { "subject": "Instalación y configuración de impresora", "title": "¡GRACIAS POR SU CONFIANZA!", - "dear": "Estimado cliente", + "dear": "Estimado cliente,", "bodyDescription": "Siga las intrucciones especificadas en este correo para llevar a cabo la instalación de la impresora.", "followGuide": "Puede utilizar como guía, el video del montaje del ribon y la cinta https://www.youtube.com/watch?v=qhb0kgQF3o8. También necesitará el QLabel, el programa para imprimir las cintas.", "downloadFrom": "Puede descargarlo desde este enlace http://www.godexintl.com/en/product/type/Download/2967", @@ -27,10 +27,5 @@ "sectionHelpDownloadRemoteSupport": "Puede descargarse el programa desde este enlace http://soporte.verdnatura.es.", "salesPersonNameText": "Soy su comercial y mi nombre es", "salesPersonPhoneText": "Teléfono y whatsapp", - "salesPersonEmailText": "Dirección de e-mail", - "actionButton": "Visita nuestra Web", - "infoButton": "Ayúdanos a mejorar", - "fiscalAddress": "VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla _ www.verdnatura.es _ clientes@verdnatura.es", - "privacy": "- AVISO - Este mensaje es privado y confidencial, y debe ser utilizado exclusivamente por la persona destinataria del mismo. Si usted ha recibido este mensaje por error, le rogamos lo comunique al remitente y borre 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.", - "privacyLaw": "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." + "salesPersonEmailText": "Dirección de e-mail" } \ No newline at end of file diff --git a/services/mailer/application/template/printer-setup/printer-setup.js b/services/mailer/application/template/printer-setup/printer-setup.js index 77147f459..6dece0d23 100644 --- a/services/mailer/application/template/printer-setup/printer-setup.js +++ b/services/mailer/application/template/printer-setup/printer-setup.js @@ -5,6 +5,7 @@ var format = require(path.join(__dirname, '../../util/format.js')); module.exports = class PrinterSetup { getData(params, cb) { let query = `SELECT + c.id clientId, CONCAT(w.name, ' ', w.firstName) name, w.phone AS phone, CONCAT(u.name, '@verdnatura.es') AS email, @@ -15,7 +16,8 @@ module.exports = class PrinterSetup { LEFT JOIN account.user u ON u.id = w.userFk JOIN country ct ON ct.id = c.countryFk WHERE c.id = ?`; - database.pool.query(query, [params.recipient], (error, result) => { + + database.pool.query(query, [params.clientId], (error, result) => { if (error || result.length == 0) return cb(new Error('No template data found')); diff --git a/services/mailer/application/template/printer-setup/static/css/style.css b/services/mailer/application/template/printer-setup/static/css/style.css new file mode 100644 index 000000000..e69de29bb diff --git a/services/mailer/application/template/sepa-core/index.html b/services/mailer/application/template/sepa-core/index.html index df149cb9f..0e9c34d04 100644 --- a/services/mailer/application/template/sepa-core/index.html +++ b/services/mailer/application/template/sepa-core/index.html @@ -7,11 +7,9 @@
- - - + + {{$.header}} +
@@ -21,53 +19,16 @@
-

{{_.dear}},

-

{{_.bodyDescription}}

+

{{_.dear}}

+

{{_.bodyDescription}}

+

{{_.conclusion}}

+ {{{previewAttachments}}}
- - - - - - - - - -
-

{{_.fiscalAddress}}

-

{{_.privacy}}

-

{{_.privacyLaw}}

-
- + + {{$.footer}} +
diff --git a/services/mailer/application/template/sepa-core/locale/es.json b/services/mailer/application/template/sepa-core/locale/es.json index 29e0ef1a9..48eaadf3a 100644 --- a/services/mailer/application/template/sepa-core/locale/es.json +++ b/services/mailer/application/template/sepa-core/locale/es.json @@ -1,11 +1,7 @@ { - "subject": "Instalación y configuración de impresora", - "title": "¡GRACIAS POR SU CONFIANZA!", - "dear": "Estimado cliente", - "bodyDescription": "A continuación le adjuntamos el documento de domicilación de datos bancarios.", - "actionButton": "Visita nuestra Web", - "infoButton": "Ayúdanos a mejorar", - "fiscalAddress": "VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla _ www.verdnatura.es _ clientes@verdnatura.es", - "privacy": "- AVISO - Este mensaje es privado y confidencial, y debe ser utilizado exclusivamente por la persona destinataria del mismo. Si usted ha recibido este mensaje por error, le rogamos lo comunique al remitente y borre 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.", - "privacyLaw": "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." + "subject": "Solicitud de domiciliación bancaria", + "title": "CAMBIOS EN SU FORMA DE PAGO", + "dear": "Estimado cliente,", + "bodyDescription": "Para poder tramitar su solicitud de cambio de su forma de pago a giro bancario, le adjuntamos los documentos correspondientes a la Ley de Pago, que tiene que cumplimentar y enviarnos.", + "conclusion": "Gracias por su atención." } \ No newline at end of file diff --git a/services/mailer/application/template/sepa-core/sepa-core.js b/services/mailer/application/template/sepa-core/sepa-core.js index 536448bf0..04fd71df7 100644 --- a/services/mailer/application/template/sepa-core/sepa-core.js +++ b/services/mailer/application/template/sepa-core/sepa-core.js @@ -5,6 +5,7 @@ var format = require(path.join(__dirname, '../../util/format.js')); module.exports = class SepaCore { getData(params, cb) { let query = `SELECT + c.id clientId, CONCAT(w.name, ' ', w.firstName) name, w.phone AS phone, CONCAT(u.name, '@verdnatura.es') AS email, @@ -15,28 +16,24 @@ module.exports = class SepaCore { LEFT JOIN account.user u ON u.id = w.userFk JOIN country ct ON ct.id = c.countryFk WHERE c.id = ?`; - database.pool.query(query, [params.recipient], (error, result) => { + + this.isPreview = params.isPreview; + this.token = params.token; + + database.pool.query(query, [params.clientId], (error, result) => { if (error || result.length == 0) return cb(new Error('No template data found')); Object.assign(this, result[0]); + cb(); }); } - get salesPersonName() { - if (this.name) - return `
${this._.salesPersonNameText}: ${this.name}
`; - } - - get salesPersonPhone() { - if (this.phone) - return `
${this._.salesPersonPhoneText}: ${format.phone(this.phone)}
`; - } - - get salesPersonEmail() { - if (this.email) - return `
${this._.salesPersonEmailText}: ` + - `${this.email}
`; + get previewAttachments() { + if (this.isPreview) + return `` + + '
Descargar adjunto
' + + 'sepa-core.pdf
'; } }; diff --git a/services/mailer/application/template/sepa-core/static/css/style.css b/services/mailer/application/template/sepa-core/static/css/style.css new file mode 100644 index 000000000..e69de29bb diff --git a/services/mailer/server/server.js b/services/mailer/server/server.js index 818b036a6..50320a391 100644 --- a/services/mailer/server/server.js +++ b/services/mailer/server/server.js @@ -1,7 +1,7 @@ var express = require('express'); var app = module.exports = express(); var bodyParser = require('body-parser'); -var settings = require('../application/settings.js'); +var config = require('../application/config.js'); var mail = require('../application/mail.js'); var database = require('../application/database.js'); var auth = require('../application/auth.js'); @@ -22,7 +22,7 @@ var requestToken = function(request, response, next) { app.use('/', requestToken, require('../application/router.js')); app.start = function() { - var listener = app.listen(settings.app().port, function() { + var listener = app.listen(config.app.port, function() { var servicePath = 'http://' + listener.address().address + ':' + listener.address().port; mail.init(); database.init(); @@ -31,7 +31,7 @@ app.start = function() { let packageJson = require('../package.json'); console.log(`Web server ${packageJson.name} listening at: ${servicePath}`); - if (settings.app().debug) { + if (config.app.debug) { console.log(`${packageJson.name} service debug mode enabled`); } }); diff --git a/services/mailer/static/css/component.css b/services/mailer/static/css/component.css new file mode 100644 index 000000000..10805e562 --- /dev/null +++ b/services/mailer/static/css/component.css @@ -0,0 +1,137 @@ +img { + margin: 0 +} + +p { + text-align: justify +} + +.wrapper { + background-color: #EEE +} + +.container { + font-family: arial, sans-serif; + max-width: 600px; + min-width: 320px; + font-size: 16px; + margin: 0 auto; + color: #555 +} + +.title { + background-color: #95d831; + text-align: center; + padding: 35px 0 +} + +.title h1 { + font-size: 32px; + color: #333; + margin: 0 +} + +.body { + background-color:#FFF; + padding: 20px +} + +.body a { + color: #8dba25 +} + +.body h1 { + color: #999 +} + +.body h3 { + font-size: 16px +} + +.panel { + border: 1px solid #DDD; + margin-bottom: 10px; + padding:10px +} + +.row { + margin-bottom: 15px; + overflow: hidden; + content: ''; + clear: both +} + +.row .text { + margin-bottom: 5px +} + +.row .control { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box +} + +.row .description { + font-size: 8px; + color: #999 +} + +.row .v-align { + padding-top: 5px; + line-height: 21px +} + +.row:last-child { + margin-bottom: 0 +} + +.row.inline .text { + margin-bottom: 0; + width: 40%; + float: left +} + +.row.inline .control { + font-weight: bold; + padding-left: 20px; + color: #000; + width: 60%; + float: left +} + +.box { + border-top: 1px solid #CCC; + border-right: 1px solid #CCC; + border-bottom: 1px solid #CCC; + font-weight: bold; + text-align: center; + padding-top: 4px; + width: 25px; + height: 21px; + color: #000; + float: left +} + +.row .control .box:first-child { + border-left: 1px solid #CCC; +} + +.attachment { + overflow: hidden; + margin-top: 10px +} + +.attachment:after { + content: ' '; + display: block; + clear: both +} + +.attachment-icon { + float: left +} + +.attachment span { + padding: 16px 0 0 10px; + float: left +} \ No newline at end of file diff --git a/services/mailer/static/images/attachment.png b/services/mailer/static/images/attachment.png new file mode 100644 index 000000000..78efe4f95 Binary files /dev/null and b/services/mailer/static/images/attachment.png differ diff --git a/services/nginx/conf-dev.conf b/services/nginx/conf-dev.conf index e36a5a82a..b35bd3757 100644 --- a/services/nginx/conf-dev.conf +++ b/services/nginx/conf-dev.conf @@ -43,6 +43,9 @@ http { location ~ ^/route(?:/(.*))?$ { proxy_pass http://127.0.0.1:3005/$1$is_args$args; } + location ~ ^/print(?:/(.*))?$ { + proxy_pass http://127.0.0.1:3006/$1$is_args$args; + } # Este tiene que ser el último location ~ ^(?:/(.*))?$ { proxy_pass http://127.0.0.1:3001/$1$is_args$args; diff --git a/services/nginx/conf-prod.conf b/services/nginx/conf-prod.conf index 32d749780..eab866aaa 100644 --- a/services/nginx/conf-prod.conf +++ b/services/nginx/conf-prod.conf @@ -38,6 +38,9 @@ http { location ~ ^/route(?:/(.*))?$ { proxy_pass http://route:3005/$1$is_args$args; } + location ~ ^/print(?:/(.*))?$ { + proxy_pass http://print:3006/$1$is_args$args; + } # Este tiene que ser el último location ~ ^(?:/(.*))?$ { proxy_pass http://salix:3001/$1$is_args$args; diff --git a/services/nginx/config.json b/services/nginx/config.json new file mode 100644 index 000000000..050b6724d --- /dev/null +++ b/services/nginx/config.json @@ -0,0 +1,14 @@ +{ + "localhost": { + "host": "localhost", + "port": 5000 + }, + "testSalix": { + "host": "test-salix", + "port": 80 + }, + "salix": { + "host": "salix", + "port": 80 + } +} \ No newline at end of file diff --git a/services/print/.gitignore b/services/print/.gitignore new file mode 100644 index 000000000..69d30d5cc --- /dev/null +++ b/services/print/.gitignore @@ -0,0 +1,5 @@ +node_modules +config.json +app.development.json +smtp.development.json +mysql.development.json diff --git a/services/print/Dockerfile b/services/print/Dockerfile new file mode 100644 index 000000000..2e8bdc43b --- /dev/null +++ b/services/print/Dockerfile @@ -0,0 +1,13 @@ +FROM node:6.9.1 + +COPY . /app + +WORKDIR /app + +RUN npm install + +RUN npm -g install pm2 + +CMD ["pm2-docker", "./server/server.js"] + +EXPOSE 3006 diff --git a/services/print/application/auth.js b/services/print/application/auth.js new file mode 100644 index 000000000..ee8f114e1 --- /dev/null +++ b/services/print/application/auth.js @@ -0,0 +1,62 @@ +var database = require('./database.js'); + +module.exports = { + +/** + * Initialize auth + * @param {Object} request - Request object + * @param {Object} response - Response object + * @param {Object} next - Next object + */ + init: function(request, response, next) { + this.request = request; + this.response = response; + this.next = next; + + this.validateToken(); + }, + +/** + * Validate auth token + */ + validateToken: function() { + let query = 'SELECT userId, ttl, created FROM salix.AccessToken WHERE id = ?'; + + database.pool.query(query, [this.getToken()], (error, result) => { + let token = result[0]; + + if (error || result.length == 0) + return this.response.status(401).send({message: 'Invalid token'}); + + if (this.isTokenExpired(token.created, token.ttl)) + return this.response.status(401).send({message: 'Token expired'}); + + this.request.userId = token.userId; + this.next(); + }); + }, + +/** + * Get request token + * @return {String} Token + */ + getToken: function() { + return this.request.headers.authorization || this.request.query.token; + }, + +/** + * Checks if the token has expired + * @param {String} created - Creation date + * @param {Integer} ttl - Ttl seconds + * @return {Boolean} True if the token has expired + */ + isTokenExpired: function(created, ttl) { + let date = new Date(created); + let currentDate = new Date(); + + date.setSeconds(date.getSeconds() + ttl); + + if (currentDate > date) + return true; + } +}; diff --git a/services/print/application/config.js b/services/print/application/config.js new file mode 100644 index 000000000..1442e85ed --- /dev/null +++ b/services/print/application/config.js @@ -0,0 +1,18 @@ +var path = require('path'); +var fs = require('fs'); +var config = {}; + +let devConfigPath = path.join(__dirname, '/config/datasources.development.json'); +let configPath = path.join(__dirname, '/config/datasources.json'); + +try { + config = Object.assign(require(configPath), require(devConfigPath)); +} catch (e) { + if (e.code == 'MODULE_NOT_FOUND') + config = require(configPath); +} + +config.proxy = require('../../nginx/config.json'); +config.package = require('../package.json'); + +module.exports = config; \ No newline at end of file diff --git a/services/print/application/config/datasources.json b/services/print/application/config/datasources.json new file mode 100644 index 000000000..93a49479a --- /dev/null +++ b/services/print/application/config/datasources.json @@ -0,0 +1,23 @@ +{ + "app": { + "port": 3006, + "debug": false, + "defaultLanguage": "es" + }, + "mysql": { + "host": "localhost", + "port": 3306, + "user": "reports", + "password": "", + "database": "" + }, + "pdf": { + "format": "A4", + "header": { + "height": "120px" + }, + "footer": { + "height": "130px" + } + } +} \ No newline at end of file diff --git a/services/print/application/config/datasources.test.json b/services/print/application/config/datasources.test.json new file mode 100644 index 000000000..92965aa5a --- /dev/null +++ b/services/print/application/config/datasources.test.json @@ -0,0 +1,19 @@ +{ + "app": { + "port": 3006, + "debug": false, + "defaultLanguage": "es" + }, + "mysql": { + "host": "localhost", + "port": 3306, + "user": "reports", + "password": "", + "database": "" + }, + "pdf": { + "footer": { + "height": "100px" + } + } +} \ No newline at end of file diff --git a/services/print/application/database.js b/services/print/application/database.js new file mode 100644 index 000000000..974540d7d --- /dev/null +++ b/services/print/application/database.js @@ -0,0 +1,24 @@ +var mysql = require('mysql'); +let config = require('./config.js'); + +module.exports = { + /** + * Pool instance + */ + pool: null, + + /** + * Start database pool + */ + init: function() { + this.pool = mysql.createPool(config.mysql); + + this.pool.getConnection(function(error, connection) { + if (error) { + throw new Error('Can\'t connect to database: ' + error.code); + } else if (config.app.debug) { + console.log('Database connection stablished'); + } + }); + } +}; diff --git a/services/print/application/locale.js b/services/print/application/locale.js new file mode 100644 index 000000000..f9737fea3 --- /dev/null +++ b/services/print/application/locale.js @@ -0,0 +1,42 @@ +var fs = require('fs'); +var config = require('./config.js'); +var path = require('path'); + +module.exports = { +/** + * Returns template locale + * @param {String} template - Template name + * @param {String} countryCode - Language code + * @param {Object} cb - Callback + */ + load: function(template, countryCode, cb) { + var localeFile = path.join(__dirname, 'template', `${template}`, 'locale', `${countryCode}.json`); + var defaultLocaleFile = path.join(__dirname, 'template', `${template}`, 'locale', `${config.app.defaultLanguage}.json`); + + fs.stat(localeFile, (error, stats) => { + if (error) { + fs.stat(defaultLocaleFile, (error, stats) => { + if (error) + return cb(new Error('Translation not found for template ' + template)); + + cb(null, {locale: require(defaultLocaleFile)}); + }); + } else { + cb(null, {locale: require(localeFile)}); + } + }); + }, + +/** + * Parse locale text + * @param {String} text - Locale text + * @param {Object} params - Locale params + * @return {String} - Returns parsed text + */ + parseText: function(text, params) { + for (var key in params) { + text = text.replace(`%${key}%`, params[key]); + } + return text; + } +}; diff --git a/services/print/application/route/manuscript.js b/services/print/application/route/manuscript.js new file mode 100644 index 000000000..90662b646 --- /dev/null +++ b/services/print/application/route/manuscript.js @@ -0,0 +1,66 @@ +var express = require('express'); +var router = new express.Router(); +var template = require('../template.js'); +var config = require('../config.js'); +var pdf = require('html-pdf'); +var path = require('path'); + +// Sepa core +router.post('/sepa-core/:clientId', function(request, response, next) { + template.get('sepa-core', {clientId: request.params.clientId}, (error, result) => { + if (error) + return response.status(400).json({message: error.message}); + + pdf.create(result.body).toStream(function(error, stream) { + if (error) + throw Error(error); + + stream.pipe(response); + }); + }); + }); + + // store pdf +/* router.post('/sepa-core/:clientId', function(request, response, next) { + template.get('sepa-core', {recipient: request.params.clientId}, (error, result) => { + if (error) + return response.status(400).json({message: error.message}); + + pdf.create(result.body).toFile('./tmp/test.pdf', function(error, result) { + if (error) + return response.status(400).json({message: error}); + + console.log(result); + return response.json(); + }); + }); + }); + */ +// Sepa core preview +router.get('/sepa-core/:clientId', function(request, response, next) { + template.get('sepa-core', {clientId: request.params.clientId}, (error, result) => { + if (error) + return response.status(400).json({message: error.message}); + + let options = config.pdf; + pdf.create(result.body, options).toStream(function(error, stream) { + if (error) + throw Error(error); + + response.setHeader('Content-Disposition', 'inline; filename="sepa-core.pdf"'); + response.setHeader('Content-type', 'application/pdf'); + stream.pipe(response); + }); + }); +}); + +router.get('/sepa-core-view/:clientId', function(request, response, next) { + template.get('sepa-core', {clientId: request.params.clientId}, (error, result) => { + if (error) + return response.status(400).json({message: error.message}); + + response.send(result.body); + }); + }); + +module.exports = router; diff --git a/services/print/application/router.js b/services/print/application/router.js new file mode 100644 index 000000000..58e9a3694 --- /dev/null +++ b/services/print/application/router.js @@ -0,0 +1,12 @@ +var express = require('express'); +var router = new express.Router(); + +// Default page +router.get('/', function(request, response) { + response.json({}); +}); + +// Manuscripts +router.use('/manuscript', require('./route/manuscript.js')); + +module.exports = router; diff --git a/services/print/application/template.js b/services/print/application/template.js new file mode 100644 index 000000000..9bc968c74 --- /dev/null +++ b/services/print/application/template.js @@ -0,0 +1,175 @@ +var fs = require('fs'); +var mustache = require('mustache'); +var locale = require('./locale.js'); +var inlineCss = require('inline-css'); +var path = require('path'); + +module.exports = { +/** + * Get template. + * @param {String} template - Template name + * @param {Object} countryCode - Language code + * @param {Object} params - Params + * @param {Object} cb - Callback + */ + get: function(template, params, cb) { + var templatePath = path.join(__dirname, 'template', `${template}`, `index.html`); + var classPath = path.join(__dirname, 'template', `${template}`, `${template}.js`); + var stylePath = path.join(__dirname, 'template', `${template}`, 'static', 'css', 'style.css'); + + fs.stat(templatePath, (error, stat) => { + if (error) + return cb(new Error('Template ' + template + ' not found')); + + let TemplateClass = require(classPath); + let instance = new TemplateClass(); + + let getRenderedStyles = (error, body) => { + if (error) + return cb(error); + + this.renderStyles(stylePath, body, (error, body) => { + if (error) + return cb(error); + + // Check if has a subject param + params.subject = params.subject || instance.subject; + + if (params.subject == undefined) { + // Try to find a subject from Html source + let title = body.match(new RegExp('(.*?)', 'i')); + + if (title) + params.subject = title[1]; + } + + this.renderImages(template, body, (error, body) => { + if (error) + return cb(error); + + cb(null, {body: body}); + }); + }); + }; + + let getDataCb = () => { + this.render(templatePath, instance, (error, result) => getRenderedStyles(error, result)); + }; + + instance.getData(params, (error, result) => { + if (error) + return cb(error); + + locale.load(template, instance.countryCode, (error, result) => { + if (error) + return cb(error); + + instance._ = result.locale; + getDataCb(null, result); + }); + }); + }); + }, + +/** + * Render template + * @param {String} path - Template path + * @param {Object} data - Params + * @param {Object} cb - Callback + */ + render: function(path, data, cb) { + fs.readFile(path, 'utf8', (error, body) => { + // Find matching sub-templates + let regexp = new RegExp(/\{\{\$\.(.*?)\}\}/, 'ig'); + let subTpl = body.match(regexp); + + if (!subTpl) { + mustache.parse(body); + return cb(null, mustache.render(body, data)); + } + + let parentBody = body; + this.renderSub(parentBody, subTpl, data, regexp, (error, body) => { + if (error) + return cb(error); + + mustache.parse(body); + cb(null, mustache.render(body, data)); + }); + }); + }, + + renderSub: function(body, subTpl, data, regexp, cb) { + let index = 1; + + subTpl.forEach(keyName => { + subTplName = keyName.replace(regexp, '$1'); + + this.get(subTplName, data, (error, result) => { + if (error) + return cb(error); + + let subTplBody = result.body; + body = body.replace(keyName, subTplBody); + + if (index === subTpl.length) + cb(null, body); + + index++; + }); + + }); + }, + +/** + * Render template style. + * @param {String} path - Stylesheet path + * @param {String} body - Rendered html + * @param {Object} cb - Callback + */ + renderStyles: function(stylePath, html, cb) { + // Common components + let comPath = path.join(__dirname, '../', 'static', 'css', 'component.css'); + + fs.readFile(comPath, 'utf8', (error, comCss) => { + fs.stat(stylePath, error => { + if (error) + return cb(new Error('Template stylesheet not found')); + + fs.readFile(stylePath, 'utf8', (error, css) => { + let style = ''; + let body = style + html; + let options = {url: ' '}; + + inlineCss(body, options) + .then(function(body) { + cb(null, body); + }); + }); + }); + }); + }, + +/** + * Render template images + * @param {String} template - Template name + * @param {String} body - template body + * @param {Object} cb - Callback + */ + renderImages: function(template, body, cb) { + let tplImages = body.match(new RegExp('src="cid:(.*?)"', 'ig')); + + if (!tplImages) + tplImages = {}; + + // Template default attachments + for (var i = 0; i < tplImages.length; i++) { + let name = tplImages[i].replace('src="cid:', '').replace('"', ''); + + let imagePath = path.join(__dirname, 'template', `${template}`, 'static', 'image', name); + body = body.replace(tplImages[i], `src="file:///${imagePath}"`); + } + + cb(null, body); + } +}; diff --git a/services/print/application/template/footer/footer.js b/services/print/application/template/footer/footer.js new file mode 100644 index 000000000..3b4020bf4 --- /dev/null +++ b/services/print/application/template/footer/footer.js @@ -0,0 +1,20 @@ +var path = require('path'); +var database = require(path.join(__dirname, '../../database.js')); +var format = require(path.join(__dirname, '../../util/format.js')); + +module.exports = class Footer { + getData(params, cb) { + let query = `SELECT + socialName + FROM client c + JOIN country ct ON ct.id = c.countryFk + WHERE c.id = ?`; + database.pool.query(query, [params.clientId], (error, result) => { + if (error || result.length == 0) + return cb(new Error('No template data found')); + + Object.assign(this, result[0]); + cb(); + }); + } +}; diff --git a/services/print/application/template/footer/index.html b/services/print/application/template/footer/index.html new file mode 100644 index 000000000..59b90b480 --- /dev/null +++ b/services/print/application/template/footer/index.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/services/print/application/template/footer/locale/es.json b/services/print/application/template/footer/locale/es.json new file mode 100644 index 000000000..04a61fddd --- /dev/null +++ b/services/print/application/template/footer/locale/es.json @@ -0,0 +1,4 @@ +{ + "phytosanitary": "VERDNATURA LEVANTE SL - Pasaporte Fitosanitario R.P. Generalitat Valenciana - Nº Comerciante: ES17462130", + "privacyLaw": "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." +} \ No newline at end of file diff --git a/services/print/application/template/footer/static/css/style.css b/services/print/application/template/footer/static/css/style.css new file mode 100644 index 000000000..8bd10148d --- /dev/null +++ b/services/print/application/template/footer/static/css/style.css @@ -0,0 +1,23 @@ +img { + margin: 0 +} + +#pageFooter { + font-family: arial, sans-serif; + font-weight: 100; + max-width: 90%; + margin: 0 auto; + font-size: 9px; + color: #555 +} + +#pageFooter .info { + border-bottom: 2px solid #CCC; + padding-bottom: 2px; + text-align: center; + font-size: 11px +} + +#pageFooter p { + text-align: center +} \ No newline at end of file diff --git a/services/print/application/template/footer/static/image/action.png b/services/print/application/template/footer/static/image/action.png new file mode 100644 index 000000000..2cd16c453 Binary files /dev/null and b/services/print/application/template/footer/static/image/action.png differ diff --git a/services/print/application/template/footer/static/image/facebook.png b/services/print/application/template/footer/static/image/facebook.png new file mode 100644 index 000000000..7ab54c538 Binary files /dev/null and b/services/print/application/template/footer/static/image/facebook.png differ diff --git a/services/print/application/template/footer/static/image/header.png b/services/print/application/template/footer/static/image/header.png new file mode 100644 index 000000000..3c063ae44 Binary files /dev/null and b/services/print/application/template/footer/static/image/header.png differ diff --git a/services/print/application/template/footer/static/image/info.png b/services/print/application/template/footer/static/image/info.png new file mode 100644 index 000000000..fb75cbc4e Binary files /dev/null and b/services/print/application/template/footer/static/image/info.png differ diff --git a/services/print/application/template/footer/static/image/instagram.png b/services/print/application/template/footer/static/image/instagram.png new file mode 100644 index 000000000..66550c4a5 Binary files /dev/null and b/services/print/application/template/footer/static/image/instagram.png differ diff --git a/services/print/application/template/footer/static/image/linkedin.png b/services/print/application/template/footer/static/image/linkedin.png new file mode 100644 index 000000000..0d191e5ae Binary files /dev/null and b/services/print/application/template/footer/static/image/linkedin.png differ diff --git a/services/print/application/template/footer/static/image/logo.png b/services/print/application/template/footer/static/image/logo.png new file mode 100644 index 000000000..55e26fec6 Binary files /dev/null and b/services/print/application/template/footer/static/image/logo.png differ diff --git a/services/print/application/template/footer/static/image/logo.svg b/services/print/application/template/footer/static/image/logo.svg new file mode 100644 index 000000000..51baf46d3 --- /dev/null +++ b/services/print/application/template/footer/static/image/logo.svg @@ -0,0 +1,48 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/services/print/application/template/footer/static/image/pinterest.png b/services/print/application/template/footer/static/image/pinterest.png new file mode 100644 index 000000000..4d7b28ef8 Binary files /dev/null and b/services/print/application/template/footer/static/image/pinterest.png differ diff --git a/services/print/application/template/footer/static/image/twitter.png b/services/print/application/template/footer/static/image/twitter.png new file mode 100644 index 000000000..c4a8ab0c1 Binary files /dev/null and b/services/print/application/template/footer/static/image/twitter.png differ diff --git a/services/print/application/template/footer/static/image/youtube.png b/services/print/application/template/footer/static/image/youtube.png new file mode 100644 index 000000000..11871deb5 Binary files /dev/null and b/services/print/application/template/footer/static/image/youtube.png differ diff --git a/services/print/application/template/header/header.js b/services/print/application/template/header/header.js new file mode 100644 index 000000000..82c78e003 --- /dev/null +++ b/services/print/application/template/header/header.js @@ -0,0 +1,20 @@ +var path = require('path'); +var database = require(path.join(__dirname, '../../database.js')); +var format = require(path.join(__dirname, '../../util/format.js')); + +module.exports = class Header { + getData(params, cb) { + let query = `SELECT + c.name AS clientName + FROM client c + JOIN country ct ON ct.id = c.countryFk + WHERE c.id = ?`; + database.pool.query(query, [params.clientId], (error, result) => { + if (error || result.length == 0) + return cb(new Error('No template data found')); + + Object.assign(this, result[0]); + cb(); + }); + } +}; diff --git a/services/print/application/template/header/index.html b/services/print/application/template/header/index.html new file mode 100644 index 000000000..30c2a21f1 --- /dev/null +++ b/services/print/application/template/header/index.html @@ -0,0 +1,7 @@ +
+ VerdNatura +

+

{{_.mercantileRegistry}}
+
{{_.fiscalAddress}}
+

+
\ No newline at end of file diff --git a/services/print/application/template/header/locale/es.json b/services/print/application/template/header/locale/es.json new file mode 100644 index 000000000..86dbe5e58 --- /dev/null +++ b/services/print/application/template/header/locale/es.json @@ -0,0 +1,4 @@ +{ + "mercantileRegistry": "CIF: B97367486 Registro Mercantil de Valencia, Tomo 8041, Libro 5334, Folio 160, sección 8, Hoja V 102076", + "fiscalAddress": "VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla · www.verdnatura.es · clientes@verdnatura.es" +} \ No newline at end of file diff --git a/services/print/application/template/header/static/css/style.css b/services/print/application/template/header/static/css/style.css new file mode 100644 index 000000000..186ede66b --- /dev/null +++ b/services/print/application/template/header/static/css/style.css @@ -0,0 +1,16 @@ +img { + margin: 0 +} + +#pageHeader-first { + border-bottom: 1px solid #DDD; + font-family: arial, sans-serif; + padding-bottom: 10px; + text-align: center; + font-size: 10px; + color: #555 +} + +.header p { + text-align: center +} \ No newline at end of file diff --git a/services/print/application/template/header/static/image/action.png b/services/print/application/template/header/static/image/action.png new file mode 100644 index 000000000..2cd16c453 Binary files /dev/null and b/services/print/application/template/header/static/image/action.png differ diff --git a/services/print/application/template/header/static/image/facebook.png b/services/print/application/template/header/static/image/facebook.png new file mode 100644 index 000000000..7ab54c538 Binary files /dev/null and b/services/print/application/template/header/static/image/facebook.png differ diff --git a/services/print/application/template/header/static/image/header.png b/services/print/application/template/header/static/image/header.png new file mode 100644 index 000000000..3c063ae44 Binary files /dev/null and b/services/print/application/template/header/static/image/header.png differ diff --git a/services/print/application/template/header/static/image/info.png b/services/print/application/template/header/static/image/info.png new file mode 100644 index 000000000..fb75cbc4e Binary files /dev/null and b/services/print/application/template/header/static/image/info.png differ diff --git a/services/print/application/template/header/static/image/instagram.png b/services/print/application/template/header/static/image/instagram.png new file mode 100644 index 000000000..66550c4a5 Binary files /dev/null and b/services/print/application/template/header/static/image/instagram.png differ diff --git a/services/print/application/template/header/static/image/linkedin.png b/services/print/application/template/header/static/image/linkedin.png new file mode 100644 index 000000000..0d191e5ae Binary files /dev/null and b/services/print/application/template/header/static/image/linkedin.png differ diff --git a/services/print/application/template/header/static/image/logo.png b/services/print/application/template/header/static/image/logo.png new file mode 100644 index 000000000..55e26fec6 Binary files /dev/null and b/services/print/application/template/header/static/image/logo.png differ diff --git a/services/print/application/template/header/static/image/logo.svg b/services/print/application/template/header/static/image/logo.svg new file mode 100644 index 000000000..51baf46d3 --- /dev/null +++ b/services/print/application/template/header/static/image/logo.svg @@ -0,0 +1,48 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/services/print/application/template/header/static/image/pinterest.png b/services/print/application/template/header/static/image/pinterest.png new file mode 100644 index 000000000..4d7b28ef8 Binary files /dev/null and b/services/print/application/template/header/static/image/pinterest.png differ diff --git a/services/print/application/template/header/static/image/twitter.png b/services/print/application/template/header/static/image/twitter.png new file mode 100644 index 000000000..c4a8ab0c1 Binary files /dev/null and b/services/print/application/template/header/static/image/twitter.png differ diff --git a/services/print/application/template/header/static/image/youtube.png b/services/print/application/template/header/static/image/youtube.png new file mode 100644 index 000000000..11871deb5 Binary files /dev/null and b/services/print/application/template/header/static/image/youtube.png differ diff --git a/services/print/application/template/sepa-core/index.html b/services/print/application/template/sepa-core/index.html new file mode 100644 index 000000000..867da43d9 --- /dev/null +++ b/services/print/application/template/sepa-core/index.html @@ -0,0 +1,128 @@ + + + + {{_.subject}} + + + + + {{$.header}} + + + +
+ + +
+

{{_.title}}

+
+ +
+
{{_.toCompleteBySupplier}}
+
+
{{_.orderReference}}
+
{{mandateCode}}
+
+
+
{{_.supplierIdentifier}}
+
ES89000B97367486
+
+
+
{{_.supplierName}}
+
{{supplierName}}
+
+
+
{{_.supplierStreet}}
+
{{supplierStreet}}
+
+
+
{{_.supplierLocation}}
+
{{supplierPostCode}}, {{supplierCity}} ({{supplierProvince}})
+
+
+
{{_.supplierCountry}}
+
{{supplierCountry}}
+
+
+ +

{{_.bodyDescription}}

+

{{_.clientAdvice}}

+ +
+
{{_.toCompleteByClient}}
+
+
{{_.clientName}}
+
{{clientName}}
+
{{_.accountHolder}}
+
+
+
{{_.clientStreet}}
+
{{clientStreet}}
+
+
+
{{_.clientLocation}}
+
{{clientPostCode}}, {{clientCity}} ({{clientProvince}})
+
+
+
{{_.clientCountry}}
+
{{clientCountry}}
+
+
+
{{_.swift}}
+
+ {{#swiftFields}} +
+ {{/swiftFields}} +
+
+
+
{{_.accountNumber}}
+
+
ES
+ {{#accountNumberFields}} +
+ {{/accountNumberFields}} +
+
+
{{_.accountNumberFormat}}
+
+
+
+
{{_.paymentType}}
+
+
+
+
+
X
+
+
{{_.recurrent}}
+
+
O
+
+
+
+
+
{{_.unique}}
+
+
+
+
+
+
{{_.signLocation}}
+
{{currentDate}}, {{supplierProvince}}
+
+
+
{{_.sign}}
+
+
+
+

{{_.mandatoryFields}}

+

{{_.sendOrder}}

+
+ + + + {{$.footer}} + + + \ No newline at end of file diff --git a/services/print/application/template/sepa-core/locale/es.json b/services/print/application/template/sepa-core/locale/es.json new file mode 100644 index 000000000..e5453e858 --- /dev/null +++ b/services/print/application/template/sepa-core/locale/es.json @@ -0,0 +1,31 @@ +{ + "title": "Orden de domiciliación de adeudo directo SEPA CORE", + "toCompleteBySupplier": "A cumplimentar por el acreedor", + "toCompleteByClient": "A cumplimentar por el deudor", + "bodyDescription": "Mediante la firma de esta orden de domiciliación, el deudor autoriza (A) al acreedor a enviar instrucciones a la entidad del deudor para adeudar su cuenta y (B) a la entidad para efectuar los adeudos en su cuenta siguiendo las instrucciones del acreedor.Como parte de sus derechos, el deudor está legitimado al reembolso por su entidad en los términos y condiciones del contrato suscrito con la misma. La solicitud de reembolso deberá efectuarse dentro de las ocho semanas que adeudo en cuenta. Puede obtener información adicional sobre sus derechos en su entidad financiera.", + "orderReference": "Referencia de la orden de domiciliación", + "supplierIdentifier": "Identificador del acreedor", + "supplierName": "Nombre del acreedor", + "supplierStreet": "Dirección", + "supplierLocation": "CP - Población - Provincia", + "supplierCountry": "País", + "clientAdvice": "Debe llevar a su Entidad Bancaria una copia del documento firmado para que lo registre y evitar la devolución.", + "clientName": "Nombre del deudor/es", + "clientStreet": "Dirección del deudor", + "clientLocation": "CP - Población - Provincia", + "clientCountry": "País del deudor", + "swift": "Swift BIC", + "accountNumber": "Número de cuenta - IBAN", + "accountHolder": "(Titular/es de la cuenta de cargo)", + "accountNumberFormat": "En España el IBAN consta de 24 posiciones comenzando siempre por ES", + "paymentType": "Tipo de pago", + "recurrent": "Recurrente", + "unique": "Único", + "signLocation": "Fecha - Localidad", + "sign": "Firma del deudor y sello", + "mandatoryFields": "TODOS LOS CAMPOS HAN DE SER CUMPLIMENTADOS OBLIGATORIAMENTE.", + "sendOrder": "UNA VEZ FIRMADA ESTA ORDEN DE DOMICILIACIÓN DEBE SER ENVIADA AL ACREEDOR PARA SU CUSTODIA Y ES RECOMENDABLE FACILITAR UNA COPIA A SU ENTIDAD BANCARIA.", + "fiscalAddress": "VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla · www.verdnatura.es · clientes@verdnatura.es", + "privacy": "- AVISO - Este mensaje es privado y confidencial, y debe ser utilizado exclusivamente por la persona destinataria del mismo. Si usted ha recibido este mensaje por error, le rogamos lo comunique al remitente y borre 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.", + "privacyLaw": "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." +} \ No newline at end of file diff --git a/services/print/application/template/sepa-core/sepa-core.js b/services/print/application/template/sepa-core/sepa-core.js new file mode 100644 index 000000000..74e08fd31 --- /dev/null +++ b/services/print/application/template/sepa-core/sepa-core.js @@ -0,0 +1,54 @@ +var path = require('path'); +var database = require(path.join(__dirname, '../../database.js')); +var format = require(path.join(__dirname, '../../util/format.js')); + +module.exports = class SepaCore { + getData(params, cb) { + let query = `SELECT + c.id clientId, + m.code mandateCode, + LOWER(ct.code) AS countryCode, + c.email AS recipient, + c.socialName AS clientName, + c.postalAddress AS clientStreet, + c.postcode AS clientPostCode, + c.city AS clientCity, + p.name AS clientProvince, + ct.country AS clientCountry, + s.name AS supplierName, + s.street AS supplierStreet, + sc.country AS supplierCountry, + s.postCode AS supplierPostCode, + s.city AS supplierCity, + sp.name AS supplierProvince + FROM client c + JOIN country ct ON ct.id = c.countryFk + LEFT JOIN province p ON p.id = c.provinceFk + LEFT JOIN mandate m ON m.clientFk = c.id AND m.finished IS NULL + LEFT JOIN supplier s ON s.id = m.companyFk + LEFT JOIN country sc ON sc.id = s.countryFk + LEFT JOIN province sp ON sp.id = s.provinceFk + WHERE c.id = ?`; + database.pool.query(query, [params.clientId], (error, result) => { + if (error || result.length == 0) + return cb(new Error('No template data found')); + + Object.assign(this, result[0]); + cb(); + }); + } + + // Swift BIC fields + get swiftFields() { + return new Array(11); + } + + // Account number fields + get accountNumberFields() { + return new Array(23); + } + + get currentDate() { + return format.date(new Date(), '/'); + } +}; diff --git a/services/print/application/template/sepa-core/static/css/style.css b/services/print/application/template/sepa-core/static/css/style.css new file mode 100644 index 000000000..5b1e628d6 --- /dev/null +++ b/services/print/application/template/sepa-core/static/css/style.css @@ -0,0 +1,22 @@ +img { + margin: 0 +} + +.body { + font-family: arial, sans-serif; + max-width: 90%; + margin: 0 auto; + font-size: 14px; + color: #000 +} + +body .title { + text-align: center; + padding-bottom: 20px +} + +body .title h1 { + font-size: 16px; + color: #333; + margin: 0 +} \ No newline at end of file diff --git a/services/print/application/template/sepa-core/static/image/action.png b/services/print/application/template/sepa-core/static/image/action.png new file mode 100644 index 000000000..2cd16c453 Binary files /dev/null and b/services/print/application/template/sepa-core/static/image/action.png differ diff --git a/services/print/application/template/sepa-core/static/image/facebook.png b/services/print/application/template/sepa-core/static/image/facebook.png new file mode 100644 index 000000000..7ab54c538 Binary files /dev/null and b/services/print/application/template/sepa-core/static/image/facebook.png differ diff --git a/services/print/application/template/sepa-core/static/image/header.png b/services/print/application/template/sepa-core/static/image/header.png new file mode 100644 index 000000000..3c063ae44 Binary files /dev/null and b/services/print/application/template/sepa-core/static/image/header.png differ diff --git a/services/print/application/template/sepa-core/static/image/info.png b/services/print/application/template/sepa-core/static/image/info.png new file mode 100644 index 000000000..fb75cbc4e Binary files /dev/null and b/services/print/application/template/sepa-core/static/image/info.png differ diff --git a/services/print/application/template/sepa-core/static/image/instagram.png b/services/print/application/template/sepa-core/static/image/instagram.png new file mode 100644 index 000000000..66550c4a5 Binary files /dev/null and b/services/print/application/template/sepa-core/static/image/instagram.png differ diff --git a/services/print/application/template/sepa-core/static/image/linkedin.png b/services/print/application/template/sepa-core/static/image/linkedin.png new file mode 100644 index 000000000..0d191e5ae Binary files /dev/null and b/services/print/application/template/sepa-core/static/image/linkedin.png differ diff --git a/services/print/application/template/sepa-core/static/image/logo.png b/services/print/application/template/sepa-core/static/image/logo.png new file mode 100644 index 000000000..55e26fec6 Binary files /dev/null and b/services/print/application/template/sepa-core/static/image/logo.png differ diff --git a/services/print/application/template/sepa-core/static/image/logo.svg b/services/print/application/template/sepa-core/static/image/logo.svg new file mode 100644 index 000000000..51baf46d3 --- /dev/null +++ b/services/print/application/template/sepa-core/static/image/logo.svg @@ -0,0 +1,48 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/services/print/application/template/sepa-core/static/image/pinterest.png b/services/print/application/template/sepa-core/static/image/pinterest.png new file mode 100644 index 000000000..4d7b28ef8 Binary files /dev/null and b/services/print/application/template/sepa-core/static/image/pinterest.png differ diff --git a/services/print/application/template/sepa-core/static/image/twitter.png b/services/print/application/template/sepa-core/static/image/twitter.png new file mode 100644 index 000000000..c4a8ab0c1 Binary files /dev/null and b/services/print/application/template/sepa-core/static/image/twitter.png differ diff --git a/services/print/application/template/sepa-core/static/image/youtube.png b/services/print/application/template/sepa-core/static/image/youtube.png new file mode 100644 index 000000000..11871deb5 Binary files /dev/null and b/services/print/application/template/sepa-core/static/image/youtube.png differ diff --git a/services/print/application/util/format.js b/services/print/application/util/format.js new file mode 100644 index 000000000..f05f53e57 --- /dev/null +++ b/services/print/application/util/format.js @@ -0,0 +1,59 @@ +let database = require('../database.js'); + +module.exports = { + + /** + * Devuelve el iban + * @param {String} addressNumber - Dirección de cuenta bancaria + * @param {Object} cb - Callback + */ + accountAddressIban: function(addressNumber, cb) { + database.pool.query('SELECT vn2008.cc_to_iban(?) AS iban', [addressNumber], function(error, result) { + cb(result[0].iban); + }); + }, + + /** + * Obtiene el numero de cuenta completo incluyendo iban + * @param {String} addressNumber - Dirección de cuenta bancaria + * @return {String} Cuenta bancaria formateada + */ + accountAddress: function(addressNumber) { + var formattedAccountAddress = addressNumber.replace(/(.{4})/g, '$1-'); + return formattedAccountAddress.substring(0, formattedAccountAddress.length - 1); + }, + + /** + * Devuelve el numero de cuenta mostrando únicamente los últimos 4 dígitos. + * @param {String} addressNumber - Dirección de cuenta bancaria + * @return {String} Cuenta bancaria formateada + */ + partialAccountAddress: function(addressNumber) { + let address = this.accountAddress(addressNumber); + return address.substring(0, 19).replace(/[0-9]/g, 'X') + address.substring(19, 24); + }, + + phone: function(number) { + return number; + }, + +/** + * Format date dd-mm-yyyy + * @param {Object} date - Date object + * @param {String} delimiter - Date delimiter + * @return {String} Formatted date + */ + date: function(date, delimiter = '-') { + let day = date.getDate(); + let month = date.getMonth() + 1; + let year = date.getFullYear(); + + if (day < 10) + day = `0${day}` + + if (month < 10) + month = `0${month}` + + return day + delimiter + month + delimiter + year; + } +}; diff --git a/services/print/application/util/system.js b/services/print/application/util/system.js new file mode 100644 index 000000000..358346184 --- /dev/null +++ b/services/print/application/util/system.js @@ -0,0 +1,10 @@ +module.exports = { + /** + * Obtiene las variables de entorno + * @param {String} env - Nombre de la variable de entorno + * @return {String} Valor de la variable de entorno + */ + getEnv: function(env) { + return process.env[env]; + } +}; diff --git a/services/print/package.json b/services/print/package.json new file mode 100644 index 000000000..c712ea1a1 --- /dev/null +++ b/services/print/package.json @@ -0,0 +1,20 @@ +{ + "name": "print", + "version": "0.0.1", + "description": "Servidor de impresión PDF", + "main": "server.js", + "dependencies": { + "body-parser": "^1.17.2", + "express": "^4.15.3", + "html-pdf": "^2.2.0", + "inline-css": "^2.2.2", + "mustache": "^2.3.0", + "mysql": "^2.13.0", + "path": "^0.12.7" + }, + "repository": { + "type": "git", + "url": "https://git.verdnatura.es/salix" + }, + "license": "GPL-3.0" +} diff --git a/services/print/server/server.js b/services/print/server/server.js new file mode 100644 index 000000000..9ef4074ea --- /dev/null +++ b/services/print/server/server.js @@ -0,0 +1,38 @@ +var express = require('express'); +var app = module.exports = express(); +var bodyParser = require('body-parser'); +var config = require('../application/config.js'); +var database = require('../application/database.js'); +var auth = require('../application/auth.js'); +var path = require('path'); + +// Body parser middleware +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({extended: true})); + +// Auth middleware +var requestToken = function(request, response, next) { + auth.init(request, response, next); +}; + +// Load routes +app.use('/', requestToken, require('../application/router.js')); + +app.start = function() { + var listener = app.listen(config.app.port, function() { + var servicePath = 'http://' + listener.address().address + ':' + listener.address().port; + database.init(); + + let packageJson = require('../package.json'); + console.log(`Web server ${packageJson.name} listening at: ${servicePath}`); + + if (config.app.debug) { + console.log(`${packageJson.name} service debug mode enabled`); + } + }); + return listener; +}; + +if (require.main === module) { + app.start(); +} diff --git a/services/print/static/css/component.css b/services/print/static/css/component.css new file mode 100644 index 000000000..e7fc24b0a --- /dev/null +++ b/services/print/static/css/component.css @@ -0,0 +1,170 @@ +body { + margin: 0 auto; + width: 210mm +} + +.panel { + border: 1px solid #DDD; + margin-bottom: 10px; + position: relative; + padding:10px +} + +.row { + margin-bottom: 15px; + overflow: hidden +} + +.row .text { + margin-bottom: 5px +} + +.row .control { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box +} + +.row .text, .row .control { + overflow: hidden +} + +.row .description { + position: relative; + padding-top: 2px; + overflow: hidden; + font-size: 8px; + display: block; + color: #999 +} + +.row .line { + border-bottom: 1px solid #DDD; + border-right: 1px solid #DDD; + border-left: 1px solid #DDD; + margin-top: 10px; + color: #999; + padding: 5px +} + +.row .description span { + background-color: #FFF; + margin: -5px 0 0 50px; + display: block; + padding: 5px; + float: left +} + +.row:last-child { + margin-bottom: 0 +} + +.row.inline .text { + margin-bottom: 0; + width: 40%; + float: left +} + +.row.inline .control { + font-weight: bold; + padding-left: 20px; + color: #000; + width: 60%; + float: left +} + +.row.inline .description { + position: static; + overflow: visible +} + +.box { + border-top: 1px solid #CCC; + border-right: 1px solid #CCC; + border-bottom: 1px solid #CCC; + font-weight: bold; + text-align: center; + padding-top: 4px; + width: 25px; + height: 21px; + color: #000; + float: left +} + +.box.crossed { + font-weight: 100; + font-size: 16px +} + +.row .control .box:first-child { + border-left: 1px solid #CCC; +} + +p { + text-align: justify +} + +.font.small { + font-size: 10px +} + +.font.verticalAlign { + height: 27px; + line-height: 27px +} + +.font.centered { + height: 27px; + line-height: 27px; + text-align: center +} + +.verticalText { + -moz-transform: rotate(90deg); + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + position: absolute; + text-align: center; + font-size: .65em; + width: 200px; + right: -115px; + top: 50% +} + +.columns:after { + display: block; + content: ' '; + clear: both +} + +.columns .size100 { + width: 100%; + float: left +} + +.columns .size75 { + width: 75%; + float: left +} + +.columns .size50 { + width: 50%; + float: left +} + +.columns .size33 { + width: 33.33%; + float: left +} + +.columns .size25 { + width: 25%; + float: left +} + + + + + + + diff --git a/services/salix/server/boot/routes.js b/services/salix/server/boot/routes.js index 2fd57778a..713f82e2a 100644 --- a/services/salix/server/boot/routes.js +++ b/services/salix/server/boot/routes.js @@ -41,6 +41,20 @@ module.exports = function(app) { }); }); + app.get('/validateToken', function(req, res) { + let token = req.headers.authorization; + + validateToken(token, function(isValid) { + if (isValid) { + res.json(null); + } else { + res.status(401).json({ + message: 'Invalid token' + }); + } + }); + }); + function validateToken(tokenId, cb) { models.AccessToken.findById(tokenId, function(err, token) { if (token) {