Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into dev
|
@ -20,12 +20,3 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- /containers/salix:/etc/salix
|
- /containers/salix:/etc/salix
|
||||||
- /mnt/storage/pdfs:/var/lib/salix/pdfs
|
- /mnt/storage/pdfs:/var/lib/salix/pdfs
|
||||||
mailer:
|
|
||||||
image: registry.verdnatura.es/salix-mailer:${TAG}
|
|
||||||
restart: unless-stopped
|
|
||||||
build:
|
|
||||||
context: services/mailer
|
|
||||||
environment:
|
|
||||||
- NODE_ENV
|
|
||||||
volumes:
|
|
||||||
- /containers/vn-mailer:/etc/vn-mailer
|
|
|
@ -1 +0,0 @@
|
||||||
node_modules
|
|
|
@ -1,6 +0,0 @@
|
||||||
FROM node:lts-slim
|
|
||||||
|
|
||||||
RUN npm -g install pm2
|
|
||||||
COPY . /app
|
|
||||||
WORKDIR /app
|
|
||||||
CMD ["pm2-docker", "./server/server.js"]
|
|
|
@ -1,75 +0,0 @@
|
||||||
var database = require('./database.js');
|
|
||||||
let config = require('./config.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'});
|
|
||||||
|
|
||||||
// Set proxy host
|
|
||||||
let proxy = config.proxy;
|
|
||||||
|
|
||||||
if (!proxy)
|
|
||||||
proxy = {
|
|
||||||
host: 'localhost',
|
|
||||||
port: 80
|
|
||||||
};
|
|
||||||
|
|
||||||
this.request.proxyHost = `http://${proxy.host}:${proxy.port}`;
|
|
||||||
this.request.user = {
|
|
||||||
id: token.userId,
|
|
||||||
token: this.getToken()
|
|
||||||
};
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,34 +0,0 @@
|
||||||
require('require-yaml');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const packageJson = require('../package.json');
|
|
||||||
let configPath = `/etc/${packageJson.name}`;
|
|
||||||
let env = process.env.NODE_ENV ? process.env.NODE_ENV : 'development';
|
|
||||||
|
|
||||||
let config = require('./config/datasources.json');
|
|
||||||
let configFiles = [
|
|
||||||
`${configPath}/datasources.json`,
|
|
||||||
`${configPath}/datasources.${env}.json`
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let configFile of configFiles) {
|
|
||||||
if (fs.existsSync(configFile))
|
|
||||||
Object.assign(config, require(configFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
let proxyConf = {};
|
|
||||||
let proxyFiles = [
|
|
||||||
'../../nginx/config.yml',
|
|
||||||
`${configPath}/config.yml`,
|
|
||||||
`${configPath}/config.${env}.yml`
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let proxyFile of proxyFiles) {
|
|
||||||
if (fs.existsSync(proxyFile))
|
|
||||||
Object.assign(proxyConf, require(proxyFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
config.proxy = proxyConf;
|
|
||||||
config.package = packageJson;
|
|
||||||
config.env = env;
|
|
||||||
|
|
||||||
module.exports = config;
|
|
|
@ -1,29 +0,0 @@
|
||||||
{
|
|
||||||
"app": {
|
|
||||||
"port": 3000,
|
|
||||||
"debug": false,
|
|
||||||
"defaultLanguage": "es",
|
|
||||||
"senderMail": "noreply@localhost",
|
|
||||||
"senderName": "MySender"
|
|
||||||
},
|
|
||||||
"mysql": {
|
|
||||||
"host": "localhost",
|
|
||||||
"port": 3306,
|
|
||||||
"database": "vn",
|
|
||||||
"user": "root",
|
|
||||||
"password": "root"
|
|
||||||
},
|
|
||||||
"smtp": {
|
|
||||||
"host": "localhost",
|
|
||||||
"port": 465,
|
|
||||||
"secure": true,
|
|
||||||
"auth": {
|
|
||||||
"user": "noreply",
|
|
||||||
"pass": ""
|
|
||||||
},
|
|
||||||
"tls": {
|
|
||||||
"rejectUnauthorized": false
|
|
||||||
},
|
|
||||||
"pool": true
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
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');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set test environment mail.
|
|
||||||
*/
|
|
||||||
testEmail: function() {
|
|
||||||
this.pool.query('SELECT fakeEmail as email FROM vn.config', function(error, qryRs) {
|
|
||||||
config.smtp.testEmail = qryRs[0].email;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,42 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,112 +0,0 @@
|
||||||
var nodemailer = require('nodemailer');
|
|
||||||
var config = require('./config.js');
|
|
||||||
var template = require('./template.js');
|
|
||||||
var database = require('./database.js');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mail module
|
|
||||||
*/
|
|
||||||
module.exports = {
|
|
||||||
transporter: null,
|
|
||||||
/**
|
|
||||||
* Load mail config.
|
|
||||||
*/
|
|
||||||
init: function() {
|
|
||||||
this.transporter = nodemailer.createTransport(config.smtp);
|
|
||||||
|
|
||||||
this.transporter.verify(function(error, success) {
|
|
||||||
if (error) {
|
|
||||||
console.error(error);
|
|
||||||
} else if (config.app.debug) {
|
|
||||||
console.log('SMTP connection stablished');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send email.
|
|
||||||
* @param {Object} recipient - Mail destinatary
|
|
||||||
* @param {String} subject - Subject
|
|
||||||
* @param {String} body - Mail body
|
|
||||||
* @param {Object} attachments - Mail attachments
|
|
||||||
* @param {Object} params - Params
|
|
||||||
* @param {Object} cb - Callback
|
|
||||||
*/
|
|
||||||
send: function(recipient, subject, body, attachments, params, cb) {
|
|
||||||
let mailOptions = {
|
|
||||||
from: '"' + config.app.senderName + '" <' + config.app.senderMail + '>',
|
|
||||||
to: recipient,
|
|
||||||
subject: subject,
|
|
||||||
html: body,
|
|
||||||
attachments
|
|
||||||
};
|
|
||||||
|
|
||||||
if (config.env != 'production')
|
|
||||||
mailOptions.to = config.app.senderMail;
|
|
||||||
|
|
||||||
this.transporter.sendMail(mailOptions, (error, info) => {
|
|
||||||
try {
|
|
||||||
let status = (error ? error.message : 'OK');
|
|
||||||
this.log(params.sender, params.recipient, recipient, subject, body, params.message, status);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return cb(new Error('Email not sent: ' + error));
|
|
||||||
|
|
||||||
if (config.app.debug)
|
|
||||||
console.log('Mail sent ' + info.messageId + ' [' + info.response + ']');
|
|
||||||
|
|
||||||
cb();
|
|
||||||
} catch (e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send email with template.
|
|
||||||
* @param {String} tplName - Template name
|
|
||||||
* @param {Object} params - Params object
|
|
||||||
* @param {Object} cb - Callback
|
|
||||||
*/
|
|
||||||
sendWithTemplate: function(tplName, params, cb) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add mail log
|
|
||||||
* @param {Integer} senderId - Sender id
|
|
||||||
* @param {Integer} recipientId - Recipient id
|
|
||||||
* @param {String} sender - Sender email
|
|
||||||
* @param {String} subject - Mail subject
|
|
||||||
* @param {String} body - Mail body
|
|
||||||
* @param {String} plainTextBody - Mail plain text body
|
|
||||||
* @param {String} status - Mail status
|
|
||||||
*/
|
|
||||||
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, config.app.senderMail, subject, body, plainTextBody, 1, status];
|
|
||||||
|
|
||||||
database.pool.query(qry, qryParams, function(error, result) {
|
|
||||||
if (config.app.debug && error)
|
|
||||||
console.log('Mail log: ' + error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,288 +0,0 @@
|
||||||
var express = require('express');
|
|
||||||
var router = new express.Router();
|
|
||||||
var config = require('../config.js');
|
|
||||||
var mail = require('../mail.js');
|
|
||||||
var template = require('../template.js');
|
|
||||||
var httpRequest = require('request');
|
|
||||||
var auth = require('../auth.js');
|
|
||||||
|
|
||||||
// Auth middleware
|
|
||||||
var requestToken = function(request, response, next) {
|
|
||||||
auth.init(request, response, next);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Printer setup
|
|
||||||
router.get('/printer-setup/:clientId', requestToken, 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/preview', requestToken, 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.get('/client-welcome/:clientId', requestToken, 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/preview', requestToken, 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.get('/sepa-core/:companyId/:clientId', requestToken, function(request, response) {
|
|
||||||
let params = {
|
|
||||||
clientId: request.params.clientId,
|
|
||||||
companyId: request.params.companyId
|
|
||||||
};
|
|
||||||
|
|
||||||
let path = `${request.proxyHost}/print/manuscript/sepa-core/${params.companyId}/${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});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (httpStream)
|
|
||||||
params.attachments = [{filename: 'sepa-core.pdf', content: httpStream}];
|
|
||||||
|
|
||||||
mail.sendWithTemplate('sepa-core', params, error => {
|
|
||||||
if (error)
|
|
||||||
return response.status(400).json({message: error.message});
|
|
||||||
|
|
||||||
return response.json();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Client SEPA CORE preview
|
|
||||||
router.get('/sepa-core/:companyId/:clientId/preview', requestToken, function(request, response) {
|
|
||||||
let params = {
|
|
||||||
clientId: request.params.clientId,
|
|
||||||
companyId: request.params.companyId,
|
|
||||||
token: request.user.token,
|
|
||||||
isPreview: true
|
|
||||||
};
|
|
||||||
|
|
||||||
template.get('sepa-core', params, (error, result) => {
|
|
||||||
if (error)
|
|
||||||
return response.status(400).json({message: error.message});
|
|
||||||
|
|
||||||
response.send(result.body);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// First debtor letter
|
|
||||||
router.get('/letter-debtor-st/:companyId/:clientId', requestToken, function(request, response) {
|
|
||||||
let params = {
|
|
||||||
clientId: request.params.clientId,
|
|
||||||
companyId: request.params.companyId,
|
|
||||||
token: request.user.token
|
|
||||||
};
|
|
||||||
|
|
||||||
let path = `${request.proxyHost}/print/manuscript/letter-debtor/${params.companyId}/${params.clientId}`;
|
|
||||||
let options = {
|
|
||||||
url: path,
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Authorization': request.user.token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let httpStream = httpRequest(options, function(error, httpResponse, body) {
|
|
||||||
if (error || httpResponse.statusCode != 200)
|
|
||||||
return response.status(400).json({message: error});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (httpStream)
|
|
||||||
params.attachments = [{filename: 'extracto.pdf', content: httpStream}];
|
|
||||||
|
|
||||||
mail.sendWithTemplate('letter-debtor-st', params, error => {
|
|
||||||
if (error)
|
|
||||||
return response.status(400).json({message: error.message});
|
|
||||||
|
|
||||||
return response.json();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// First debtor letter preview
|
|
||||||
router.get('/letter-debtor-st/:companyId/:clientId/preview', requestToken, function(request, response) {
|
|
||||||
template.get('letter-debtor-st', {
|
|
||||||
clientId: request.params.clientId,
|
|
||||||
companyId: request.params.companyId,
|
|
||||||
token: request.user.token,
|
|
||||||
isPreview: true
|
|
||||||
}, (error, result) => {
|
|
||||||
if (error)
|
|
||||||
return response.status(400).json({message: error.message});
|
|
||||||
|
|
||||||
response.send(result.body);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Second debtor letter
|
|
||||||
router.get('/letter-debtor-nd/:companyId/:clientId', requestToken, function(request, response) {
|
|
||||||
let params = {
|
|
||||||
clientId: request.params.clientId,
|
|
||||||
companyId: request.params.companyId,
|
|
||||||
token: request.user.token
|
|
||||||
};
|
|
||||||
|
|
||||||
let path = `${request.proxyHost}/print/manuscript/letter-debtor/${params.companyId}/${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});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (httpStream)
|
|
||||||
params.attachments = [{filename: 'extracto.pdf', content: httpStream}];
|
|
||||||
|
|
||||||
mail.sendWithTemplate('letter-debtor-nd', params, error => {
|
|
||||||
if (error)
|
|
||||||
return response.status(400).json({message: error.message});
|
|
||||||
|
|
||||||
return response.json();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Second debtor letter preview
|
|
||||||
router.get('/letter-debtor-nd/:companyId/:clientId/preview', requestToken, function(request, response) {
|
|
||||||
template.get('letter-debtor-nd', {
|
|
||||||
clientId: request.params.clientId,
|
|
||||||
companyId: request.params.companyId,
|
|
||||||
token: request.user.token,
|
|
||||||
isPreview: true
|
|
||||||
}, (error, result) => {
|
|
||||||
if (error)
|
|
||||||
return response.status(400).json({message: error.message});
|
|
||||||
|
|
||||||
response.send(result.body);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Payment method changes
|
|
||||||
router.get('/payment-update/:clientId', requestToken, 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.get('/client-deactivate/:clientId', requestToken, function(request, response) {
|
|
||||||
var params = {
|
|
||||||
alias: 'creditInsurance',
|
|
||||||
code: 'clientDeactivate',
|
|
||||||
bodyParams: {
|
|
||||||
clientId: request.params.clientId
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
mail.sendWithTemplate('notification-alias', params, error => {
|
|
||||||
if (error)
|
|
||||||
response.status(400).json({message: error.message});
|
|
||||||
|
|
||||||
return response.json();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
|
||||||
|
|
||||||
|
|
||||||
// Single user notification
|
|
||||||
/* router.post('/:recipient/noticeUserSend', function(request, response) {
|
|
||||||
var params = {
|
|
||||||
recipient: request.params.recipient,
|
|
||||||
sender: request.body.sender,
|
|
||||||
category: request.body.category,
|
|
||||||
message: request.body.message
|
|
||||||
};
|
|
||||||
|
|
||||||
if (params.sender == params.recipient)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let query = `SELECT COUNT(*) isEnabled
|
|
||||||
FROM noticeSubscription s
|
|
||||||
JOIN noticeCategory c ON c.id = s.noticeCategoryFk AND c.isEnabled
|
|
||||||
WHERE c.keyName = ? AND s.userFk = ?`;
|
|
||||||
|
|
||||||
database.pool.query(query, [params.category, params.recipient, params.sender], (error, result) => {
|
|
||||||
mail.sendWithTemplate('notification-notice', params, result => {
|
|
||||||
return response.json(result);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}); */
|
|
||||||
|
|
||||||
// Send notification to all user subscribed to category
|
|
||||||
/* router.post('/:category/noticeCategorySend', function(request, response) {
|
|
||||||
var params = {
|
|
||||||
sender: request.body.sender,
|
|
||||||
category: request.params.category,
|
|
||||||
message: request.body.message
|
|
||||||
};
|
|
||||||
|
|
||||||
let query = `SELECT s.userFk id FROM noticeSubscription s JOIN noticeCategory
|
|
||||||
c ON c.id = s.noticeCategoryFk AND c.isEnabled WHERE c.keyName = ? AND s.userFk != ?`;
|
|
||||||
|
|
||||||
database.pool.query(query, [params.category, params.sender], (error, result) => {
|
|
||||||
result.forEach(function(user) {
|
|
||||||
params.recipient = user.id;
|
|
||||||
|
|
||||||
mail.sendWithTemplate('notification-notice', params, result => {
|
|
||||||
return response.json(result);
|
|
||||||
});
|
|
||||||
}, this);
|
|
||||||
});
|
|
||||||
}); */
|
|
||||||
|
|
||||||
// Send system notification
|
|
||||||
/* router.post('/:recipient/noticeSystemSend', function(request, response) {
|
|
||||||
var params = {
|
|
||||||
recipient: request.params.recipient,
|
|
||||||
sender: settings.smtp().auth.id,
|
|
||||||
category: request.body.category,
|
|
||||||
message: request.body.message
|
|
||||||
};
|
|
||||||
|
|
||||||
mail.sendWithTemplate('notification-notice', params, result => {
|
|
||||||
return response.json(result);
|
|
||||||
});
|
|
||||||
}); */
|
|
|
@ -1,49 +0,0 @@
|
||||||
var express = require('express');
|
|
||||||
var router = new express.Router();
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
// Mailer default page
|
|
||||||
router.get('/', function(request, response) {
|
|
||||||
response.json({});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Notifications
|
|
||||||
router.use('/notification', require('./route/notification.js'));
|
|
||||||
|
|
||||||
// Serve static images
|
|
||||||
router.use('/static/:template/:image', function(request, response) {
|
|
||||||
let imagePath = path.join(__dirname, '/template/', request.params.template, '/image/', request.params.image);
|
|
||||||
|
|
||||||
fs.stat(imagePath, function(error) {
|
|
||||||
if (error)
|
|
||||||
return response.json({message: 'Image not found'});
|
|
||||||
|
|
||||||
let readStream = fs.createReadStream(imagePath);
|
|
||||||
|
|
||||||
readStream.on('open', function() {
|
|
||||||
let contentType = getContentType(imagePath);
|
|
||||||
|
|
||||||
if (contentType)
|
|
||||||
response.setHeader('Content-type', getContentType(imagePath));
|
|
||||||
|
|
||||||
readStream.pipe(response);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function getContentType(path) {
|
|
||||||
let types = {
|
|
||||||
png: 'image/png',
|
|
||||||
svg: 'image/svg+xml',
|
|
||||||
gif: 'image/gif',
|
|
||||||
jpeg: 'image/jpeg',
|
|
||||||
jpg: 'image/jpeg'
|
|
||||||
};
|
|
||||||
|
|
||||||
let extension = path.split('.')[1];
|
|
||||||
|
|
||||||
return types[extension];
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = router;
|
|
|
@ -1,234 +0,0 @@
|
||||||
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}`, '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('<title>(.*?)</title>', 'i'));
|
|
||||||
|
|
||||||
if (title)
|
|
||||||
params.subject = title[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getAttachments(template, body, params.isPreview, (error, result) => {
|
|
||||||
if (error)
|
|
||||||
return cb(error);
|
|
||||||
|
|
||||||
cb(null, {recipient: instance.recipient, subject: params.subject, body: result.body, attachments: result.attachments});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
instance.isPreview = params.isPreview;
|
|
||||||
|
|
||||||
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));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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++;
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 = '<style>' + comCss + css + '</style>';
|
|
||||||
let body = style + html;
|
|
||||||
let options = {url: ' '};
|
|
||||||
|
|
||||||
inlineCss(body, options)
|
|
||||||
.then(function(body) {
|
|
||||||
cb(null, body);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get template attachments
|
|
||||||
* @param {String} template - Template name
|
|
||||||
* @param {String} body - template body
|
|
||||||
* @param {Object} cb - Callback
|
|
||||||
*/
|
|
||||||
getAttachments: function(template, body, isPreview, cb) {
|
|
||||||
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 src = tplAttachments[i].replace('src="cid:', '').replace('"', '').split('/');
|
|
||||||
let attachmentTpl = src[0];
|
|
||||||
let attachment = src[1];
|
|
||||||
|
|
||||||
if (isPreview) {
|
|
||||||
let attachmentPath = `/mailer/static/${attachmentTpl}/${attachment}`;
|
|
||||||
body = body.replace(tplAttachments[i], `src="${attachmentPath}"`);
|
|
||||||
} else {
|
|
||||||
let attachmentPath = path.join(__dirname, 'template', `${attachmentTpl}`, 'image', attachment);
|
|
||||||
let attachmentName = attachmentTpl + '/' + attachment;
|
|
||||||
attachments.push({filename: attachmentName, path: attachmentPath, cid: attachmentName});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPreview)
|
|
||||||
return cb(null, {body: body, attachments: attachments});
|
|
||||||
|
|
||||||
// Template attachment files
|
|
||||||
let attachmentsPath = path.join(__dirname, 'template', `${template}`, 'attachment.json');
|
|
||||||
|
|
||||||
fs.stat(attachmentsPath, (error, stats) => {
|
|
||||||
if (error)
|
|
||||||
return cb(null, {body: body, attachments: attachments});
|
|
||||||
|
|
||||||
let attachObj = require(attachmentsPath);
|
|
||||||
|
|
||||||
for (var i = 0; i < attachObj.length; i++) {
|
|
||||||
let filename = attachObj[i];
|
|
||||||
let attachmentPath = path.join(__dirname, 'template', `${template}`, 'attachment', filename);
|
|
||||||
|
|
||||||
attachments.push({filename: filename, path: attachmentPath, cid: filename});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.checkAttachments(attachments, error => {
|
|
||||||
if (error)
|
|
||||||
return cb(error);
|
|
||||||
cb(null, {body: body, attachments: attachments});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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];
|
|
||||||
fs.stat(attachment.path, error => {
|
|
||||||
if (error)
|
|
||||||
return cb(new Error(`Could not load attachment file ${attachment.path}`));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cb();
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
[]
|
|
|
@ -1,46 +0,0 @@
|
||||||
var path = require('path');
|
|
||||||
var database = require(path.join(__dirname, '../../database.js'));
|
|
||||||
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,
|
|
||||||
u.name AS userName,
|
|
||||||
LOWER(ct.code) countryCode,
|
|
||||||
c.email recipient
|
|
||||||
FROM client c
|
|
||||||
JOIN account.user u ON u.id = c.id
|
|
||||||
LEFT JOIN worker w ON w.id = c.salesPersonFk
|
|
||||||
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.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 `<div>${this._.salesPersonNameText}: <strong>${this.name}</strong></div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
get salesPersonPhone() {
|
|
||||||
if (this.phone)
|
|
||||||
return `<div>${this._.salesPersonPhoneText}: <strong>${format.phone(this.phone)}</strong></div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
get salesPersonEmail() {
|
|
||||||
if (this.email)
|
|
||||||
return `<div>${this._.salesPersonEmailText}: ` +
|
|
||||||
`<strong><a href="mailto:${this.email}" target="_blank" style="color:#8dba25">${this.email}</strong></div>`;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,67 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="es">
|
|
||||||
<head>
|
|
||||||
<title>{{_.subject}}</title>
|
|
||||||
<meta charset="utf8"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="container">
|
|
||||||
<!-- Header block -->
|
|
||||||
{{$.header}}
|
|
||||||
<!-- Header block end -->
|
|
||||||
|
|
||||||
<!-- Title block -->
|
|
||||||
<div class="title">
|
|
||||||
<h1>{{_.title}}</h1>
|
|
||||||
</div>
|
|
||||||
<!-- Title block end -->
|
|
||||||
|
|
||||||
<!-- Mail body block -->
|
|
||||||
<div class="body">
|
|
||||||
<p>{{_.dear}}</p>
|
|
||||||
<p>{{{_.bodyDescription}}}</p>
|
|
||||||
<p>
|
|
||||||
<div>{{_.clientNumber}} <strong>{{clientId}}</strong></div>
|
|
||||||
<div>{{_.user}} <strong>{{userName}}</strong></div>
|
|
||||||
<div>{{_.password}} <strong>********</strong> {{{_.passwordResetText}}}</div>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h1>{{_.sectionHowToBuyTitle}}</h1>
|
|
||||||
<p>{{_.sectionHowToBuyDescription}}</p>
|
|
||||||
<ol>
|
|
||||||
<li>{{_.sectionHowToBuyRequeriment1}}</li>
|
|
||||||
<li>{{_.sectionHowToBuyRequeriment2}}</li>
|
|
||||||
<li>{{_.sectionHowToBuyRequeriment3}}</li>
|
|
||||||
</ol>
|
|
||||||
<p>{{_.sectionHowToBuyStock}}</p>
|
|
||||||
<p>{{_.sectionHowToBuyDelivery}}</p>
|
|
||||||
|
|
||||||
<h1>{{_.sectionHowToPayTitle}}</h1>
|
|
||||||
<p>{{_.sectionHowToPayDescription}}</p>
|
|
||||||
<ul>
|
|
||||||
<li>{{{_.sectionHowToPayOption1}}}</li>
|
|
||||||
<li>{{{_.sectionHowToPayOption2}}}</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h1>{{_.sectionToConsiderTitle}}</h1>
|
|
||||||
<p>{{_.sectionToConsiderDescription}}</p>
|
|
||||||
|
|
||||||
<h3>{{_.sectionClaimsPolicyTitle}}</h3>
|
|
||||||
<p>{{_.sectionClaimsPolicyDescription}}</p>
|
|
||||||
<p>{{{_.doubtsText}}}</p>
|
|
||||||
<p>
|
|
||||||
{{{salesPersonName}}}
|
|
||||||
{{{salesPersonPhone}}}
|
|
||||||
{{{salesPersonEmail}}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<!-- Mail body block end -->
|
|
||||||
|
|
||||||
<!-- Footer block -->
|
|
||||||
{{$.footer}}
|
|
||||||
<!-- Footer block end -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,29 +0,0 @@
|
||||||
{
|
|
||||||
"subject": "¡Le damos la bienvenida!",
|
|
||||||
"title": "¡LE DAMOS LA BIENVENIDA!",
|
|
||||||
"dear": "Estimado cliente,",
|
|
||||||
"bodyDescription": "Sus datos para poder comprar en la web de verdnatura (<a href=\"https://www.verdnatura.es\" title=\"Visitar Verdnatura\" target=\"_blank\">https://www.verdnatura.es</a>) o en nuestras aplicaciones para <a href=\"https://goo.gl/3hC2mG\" title=\"App Store\" target=\"_blank\">iOS</a> y <a href=\"https://goo.gl/8obvLc\" title=\"Google Play\" target=\"_blank\">Android</a> (<a href=\"https://www.youtube.com/watch?v=gGfEtFm8qkw\" target=\"_blank\"><strong>Ver tutorial de uso</strong></a>), son:",
|
|
||||||
"clientNumber": "Identificador de cliente:",
|
|
||||||
"user": "Usuario:",
|
|
||||||
"password": "Contraseña:",
|
|
||||||
"passwordResetText": "(<a href=\"https://verdnatura.es\">Haga clic en \"¿Has olvidado tu contraseña?\"</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 <strong>tarjeta</strong> a través de nuestra plataforma web (al confirmar el pedido).",
|
|
||||||
"sectionHowToPayOption2": "Mediante <strong>giro bancario mensual</strong>, 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, <strong>¡estamos para atenderle!</strong>",
|
|
||||||
"salesPersonNameText": "Soy tu comercial y mi nombre es",
|
|
||||||
"salesPersonPhoneText": "Teléfono y whatsapp",
|
|
||||||
"salesPersonEmailText": "Dirección de e-mail"
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
<svg fill="#000000" height="48" viewBox="0 0 24 24" width="48" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
|
|
||||||
<path d="M0 0h24v24H0z" fill="none"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 205 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg fill="#000000" height="48" viewBox="0 0 24 24" width="48" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M0 0h24v24H0z" fill="none"/>
|
|
||||||
<path d="M11.5 9C10.12 9 9 10.12 9 11.5s1.12 2.5 2.5 2.5 2.5-1.12 2.5-2.5S12.88 9 11.5 9zM20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-3.21 14.21l-2.91-2.91c-.69.44-1.51.7-2.39.7C9.01 16 7 13.99 7 11.5S9.01 7 11.5 7 16 9.01 16 11.5c0 .88-.26 1.69-.7 2.39l2.91 2.9-1.42 1.42z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 461 B |
|
@ -1,21 +0,0 @@
|
||||||
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,
|
|
||||||
LOWER(ct.code) countryCode
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.4 KiB |
|
@ -1,42 +0,0 @@
|
||||||
<!-- Action button block -->
|
|
||||||
<div class="buttons">
|
|
||||||
<a href="https://www.verdnatura.es" target="_blank"><div class="btn">
|
|
||||||
<span class="text">{{_.actionButton}}</span>
|
|
||||||
<span class="icon"><img src="cid:footer/action.png"/></span>
|
|
||||||
</div></a><a href="https://goo.gl/forms/j8WSL151ZW6QtlT72" target="_blank"><div class="btn">
|
|
||||||
<span class="text">{{_.infoButton}}</span>
|
|
||||||
<span class="icon"><img src="cid:footer/info.png"/></span>
|
|
||||||
</div></a>
|
|
||||||
</div>
|
|
||||||
<!-- Action button block -->
|
|
||||||
|
|
||||||
<!-- Networks block -->
|
|
||||||
<div class="footer">
|
|
||||||
<a href="https://www.facebook.com/Verdnatura" target="_blank">
|
|
||||||
<img src="cid:footer/facebook.png" alt="Facebook"/>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.twitter.com/Verdnatura" target="_blank">
|
|
||||||
<img src="cid:footer/twitter.png" alt="Twitter"/>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.youtube.com/Verdnatura" target="_blank">
|
|
||||||
<img src="cid:footer/youtube.png" alt="Youtube"/>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.pinterest.com/Verdnatura" target="_blank">
|
|
||||||
<img src="cid:footer/pinterest.png" alt="Pinterest"/>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.instagram.com/Verdnatura" target="_blank">
|
|
||||||
<img src="cid:footer/instagram.png" alt="Instagram"/>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.linkedin.com/company/verdnatura" target="_blank">
|
|
||||||
<img src="cid:footer/linkedin.png" alt="Linkedin"/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<!-- Networks block end -->
|
|
||||||
|
|
||||||
<!-- Privacy block -->
|
|
||||||
<div class="privacy">
|
|
||||||
<p>{{_.fiscalAddress}}</p>
|
|
||||||
<p>{{_.privacy}}</p>
|
|
||||||
<p>{{_.privacyLaw}}</p>
|
|
||||||
</div>
|
|
||||||
<!-- Privacy block end -->
|
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
"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."
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
"actionButton": "Visitez notre site web",
|
|
||||||
"infoButton": "Aidez-nous à améliorer",
|
|
||||||
"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."
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
.buttons {
|
|
||||||
background-color: #FFF;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons a {
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #fff
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons .btn {
|
|
||||||
background-color: #333;
|
|
||||||
min-width: 300px;
|
|
||||||
height: 72px;
|
|
||||||
display: inline-block;
|
|
||||||
text-align: center
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons .btn .text {
|
|
||||||
display: inline-block;
|
|
||||||
padding-top: 22px
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons .btn .icon {
|
|
||||||
background-color: #95d831;
|
|
||||||
text-align: center;
|
|
||||||
padding-top: 22px;
|
|
||||||
float: right;
|
|
||||||
height: 50px;
|
|
||||||
width: 70px
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
background-color: #555;
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px 0
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer a {
|
|
||||||
text-decoration: none;
|
|
||||||
margin-right: 5px
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer a img {
|
|
||||||
margin: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
.privacy {
|
|
||||||
padding: 20px 0;
|
|
||||||
font-size: 10px;
|
|
||||||
font-weight: 100
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
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,
|
|
||||||
LOWER(ct.code) countryCode
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
Before Width: | Height: | Size: 30 KiB |
|
@ -1,3 +0,0 @@
|
||||||
<div>
|
|
||||||
<a href="https://www.verdnatura.es"/><img src="cid:header/logo.png" alt="VerdNatura"/></a>
|
|
||||||
</div>
|
|
|
@ -1,2 +0,0 @@
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
.banner img {
|
|
||||||
width: 100%
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="es">
|
|
||||||
<head>
|
|
||||||
<title>{{_.subject}}</title>
|
|
||||||
<meta charset="utf8"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="container">
|
|
||||||
<!-- Header block -->
|
|
||||||
{{$.header}}
|
|
||||||
<!-- Header block end -->
|
|
||||||
|
|
||||||
<!-- Title block -->
|
|
||||||
<div class="title">
|
|
||||||
<h1>{{_.title}}</h1>
|
|
||||||
</div>
|
|
||||||
<!-- Title block end -->
|
|
||||||
|
|
||||||
<!-- Mail body block -->
|
|
||||||
<div class="body">
|
|
||||||
<p>{{_.dear}}</p>
|
|
||||||
|
|
||||||
<p>{{_.bodyDescription}}</p>
|
|
||||||
<p>{{_.termLimits}}</p>
|
|
||||||
<p>
|
|
||||||
{{_.payMethod}}
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li>{{_.payMethodOption1}}</li>
|
|
||||||
<li>{{_.payMethodOption2}}</li>
|
|
||||||
</ol>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{{_.legalActions}}
|
|
||||||
<ol type="a">
|
|
||||||
<li>{{_.legalActionsOption1}}</li>
|
|
||||||
<li>{{_.legalActionsOption2}}</li>
|
|
||||||
<li>{{_.legalActionsOption3}}</li>
|
|
||||||
</ol>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>{{_.contact}}</p>
|
|
||||||
|
|
||||||
<p>{{_.waitingForNews}}</p>
|
|
||||||
<p>{{_.conclusion}}</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<div class="row">
|
|
||||||
<div class="text">{{bankName}}</div>
|
|
||||||
<div class="control">{{iban}}</div>
|
|
||||||
<div class="description">
|
|
||||||
<div class="line"><span>{{_.accountTransferData}}</span></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{{#isPreview}}
|
|
||||||
<a href="/print/manuscript/letter-debtor/{{companyId}}/{{clientId}}/preview?token={{token}}" target="_blank" title="Ver adjunto">
|
|
||||||
<div class="attachment">
|
|
||||||
<div class="attachment-icon">
|
|
||||||
<img src="cid:default/preview.svg" alt="Ver adjunto"/>
|
|
||||||
</div>
|
|
||||||
<span>Ver adjunto</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/print/manuscript/letter-debtor/{{companyId}}/{{clientId}}?token={{token}}" target="_blank" title="Descargar adjunto">
|
|
||||||
<div class="attachment">
|
|
||||||
<div class="attachment-icon">
|
|
||||||
<img src="cid:default/download.svg" alt="Descargar adjunto"/>
|
|
||||||
</div>
|
|
||||||
<span>Descargar PDF</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
{{/isPreview}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Mail body block end -->
|
|
||||||
|
|
||||||
<!-- Footer block -->
|
|
||||||
{{$.footer}}
|
|
||||||
<!-- Footer block end -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,39 +0,0 @@
|
||||||
var path = require('path');
|
|
||||||
var database = require(path.join(__dirname, '../../database.js'));
|
|
||||||
var format = require(path.join(__dirname, '../../util/format.js'));
|
|
||||||
|
|
||||||
module.exports = class LetterDebtorNd {
|
|
||||||
getData(params, cb) {
|
|
||||||
let query = `SELECT
|
|
||||||
sa.iban,
|
|
||||||
be.name AS bankName,
|
|
||||||
LOWER(ct.code) countryCode,
|
|
||||||
c.email recipient
|
|
||||||
FROM client c
|
|
||||||
JOIN company AS cny
|
|
||||||
JOIN supplierAccount AS sa ON sa.id = cny.supplierAccountFk
|
|
||||||
JOIN bankEntity be ON be.id = sa.bankEntityFk
|
|
||||||
JOIN country ct ON ct.id = c.countryFk
|
|
||||||
WHERE c.id = ? AND cny.id = ?`;
|
|
||||||
|
|
||||||
this.clientId = params.clientId;
|
|
||||||
this.companyId = params.companyId;
|
|
||||||
this.token = params.token;
|
|
||||||
|
|
||||||
database.pool.query(query, [params.clientId, params.companyId], (error, result) => {
|
|
||||||
if (error || result.length == 0)
|
|
||||||
return cb(new Error('No template data found'));
|
|
||||||
|
|
||||||
Object.assign(this, result[0]);
|
|
||||||
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get previewAttachments() {
|
|
||||||
if (this.isPreview)
|
|
||||||
return `<a href="/print/manuscript/letter-debtor/${this.companyId}/${this.clientId}/preview?token=${this.token}" target="_blank" title="Ver extracto.pdf">` +
|
|
||||||
'<div class="attachment"><div class="attachment-icon"><img src="cid:attachment.png" alt="Descargar adjunto"/></div>' +
|
|
||||||
'<span>extracto.pdf</span></div></a>';
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"subject": "Reiteración de aviso por saldo deudor",
|
|
||||||
"title": "AVISO REITERADO",
|
|
||||||
"dear": "Estimado cliente,",
|
|
||||||
"bodyDescription": "Nos dirigimos a Vd. nuevamente para informarle que sigue pendiente su deuda con nuestra empresa, tal y como puede comprobar en el extracto adjunto.",
|
|
||||||
"termLimits": "Dado que los plazos de pago acordados están ampliamente superados, no procede mayor dilación en la liquidación del importe adeudado.",
|
|
||||||
"payMethod": "Para ello dispone de las siguientes formas de pago:",
|
|
||||||
"payMethodOption1": "Pago online desde nuestra web",
|
|
||||||
"payMethodOption2": "Ingreso o transferencia al número de cuenta que detallamos al pie de esta carta, indicando el número de cliente.",
|
|
||||||
"legalActions": "En caso de no ser atendido este apremio de pago, nos veremos obligados a iniciar las acciones legales que procedan, entre las que están:",
|
|
||||||
"legalActionsOption1": "Inclusión en ficheros negativos sobre solvencia patrimonial y crédito.",
|
|
||||||
"legalActionsOption2": "Reclamación judicial",
|
|
||||||
"legalActionsOption3": "Cesión de deuda a una empresa de gestión de cobro",
|
|
||||||
"contact": "Para consultas, puede ponerse en contacto con nosotros en el 96 324 21 00.",
|
|
||||||
"waitingForNews": "En espera de sus noticias",
|
|
||||||
"conclusion": "Gracias por su atención.",
|
|
||||||
"accountTransferData": "Datos para transferencia bancaria"
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="es">
|
|
||||||
<head>
|
|
||||||
<title>{{_.subject}}</title>
|
|
||||||
<meta charset="utf8"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="container">
|
|
||||||
<!-- Header block -->
|
|
||||||
{{$.header}}
|
|
||||||
<!-- Header block end -->
|
|
||||||
|
|
||||||
<!-- Title block -->
|
|
||||||
<div class="title">
|
|
||||||
<h1>{{_.title}}</h1>
|
|
||||||
</div>
|
|
||||||
<!-- Title block end -->
|
|
||||||
|
|
||||||
<!-- Mail body block -->
|
|
||||||
<div class="body">
|
|
||||||
<p>{{_.dear}}</p>
|
|
||||||
|
|
||||||
<p>{{_.bodyDescription}}</p>
|
|
||||||
<p>{{_.viewExtract}}</p>
|
|
||||||
<p>{{_.validData}}</p>
|
|
||||||
<p>{{_.payMethod}}</p>
|
|
||||||
<p>{{_.conclusion}}</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<div class="row">
|
|
||||||
<div class="text">{{bankName}}</div>
|
|
||||||
<div class="control">{{iban}}</div>
|
|
||||||
<div class="description">
|
|
||||||
<div class="line"><span>{{_.accountTransferData}}</span></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{{#isPreview}}
|
|
||||||
<a href="/print/manuscript/letter-debtor/{{companyId}}/{{clientId}}/preview?token={{token}}" target="_blank" title="Ver adjunto">
|
|
||||||
<div class="attachment">
|
|
||||||
<div class="attachment-icon">
|
|
||||||
<img src="cid:default/preview.svg" alt="Ver adjunto"/>
|
|
||||||
</div>
|
|
||||||
<span>Ver adjunto</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/print/manuscript/letter-debtor/{{companyId}}/{{clientId}}?token={{token}}" target="_blank" title="Descargar adjunto">
|
|
||||||
<div class="attachment">
|
|
||||||
<div class="attachment-icon">
|
|
||||||
<img src="cid:default/download.svg" alt="Descargar adjunto"/>
|
|
||||||
</div>
|
|
||||||
<span>Descargar PDF</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
{{/isPreview}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Mail body block end -->
|
|
||||||
|
|
||||||
<!-- Footer block -->
|
|
||||||
{{$.footer}}
|
|
||||||
<!-- Footer block end -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,39 +0,0 @@
|
||||||
var path = require('path');
|
|
||||||
var database = require(path.join(__dirname, '../../database.js'));
|
|
||||||
var format = require(path.join(__dirname, '../../util/format.js'));
|
|
||||||
|
|
||||||
module.exports = class LetterDebtorSt {
|
|
||||||
getData(params, cb) {
|
|
||||||
let query = `SELECT
|
|
||||||
sa.iban,
|
|
||||||
be.name AS bankName,
|
|
||||||
LOWER(ct.code) countryCode,
|
|
||||||
c.email recipient
|
|
||||||
FROM client c
|
|
||||||
JOIN company AS cny
|
|
||||||
JOIN supplierAccount AS sa ON sa.id = cny.supplierAccountFk
|
|
||||||
JOIN bankEntity be ON be.id = sa.bankEntityFk
|
|
||||||
JOIN country ct ON ct.id = c.countryFk
|
|
||||||
WHERE c.id = ? AND cny.id = ?`;
|
|
||||||
|
|
||||||
this.clientId = params.clientId;
|
|
||||||
this.companyId = params.companyId;
|
|
||||||
this.token = params.token;
|
|
||||||
|
|
||||||
database.pool.query(query, [params.clientId, params.companyId], (error, result) => {
|
|
||||||
if (error || result.length == 0)
|
|
||||||
return cb(new Error('No template data found'));
|
|
||||||
|
|
||||||
Object.assign(this, result[0]);
|
|
||||||
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get previewAttachments() {
|
|
||||||
if (this.isPreview)
|
|
||||||
return `<a href="/print/manuscript/letter-debtor/${this.companyId}/${this.clientId}/preview?token=${this.token}" target="_blank" title="Ver extracto.pdf">` +
|
|
||||||
'<div class="attachment"><div class="attachment-icon"><img src="cid:attachment.png" alt="Descargar adjunto"/></div>' +
|
|
||||||
'<span>extracto.pdf</span></div></a>';
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"subject": "Aviso inicial por saldo deudor",
|
|
||||||
"title": "AVISO INICIAL",
|
|
||||||
"dear": "Estimado cliente,",
|
|
||||||
"bodyDescription": "Por el presente escrito le comunicamos que, según nuestros datos contables, su cuenta tiene un saldo pendiente de liquidar.",
|
|
||||||
"viewExtract": "Le solicitamos compruebe que el extracto adjunto corresponde con los datos de que Vd. dispone. Nuestro departamento de administración le aclarará gustosamente cualquier duda que pueda tener, e igualmente le facilitará cualquier documento que solicite.",
|
|
||||||
"validData": "Si al comprobar los datos aportados resultaran correctos, le rogamos proceda a regularizar su situación.",
|
|
||||||
"payMethod": "Si no desea desplazarse personalmente hasta nuestras oficinas, puede realizar el pago mediante transferencia bancaria a la cuenta que figura al pie del comunicado, indicando su número de cliente, o bien puede realizar el pago online desde nuestra página web.",
|
|
||||||
"conclusion": "De antemano le agradecemos su amable colaboración.",
|
|
||||||
"accountTransferData": "Datos para transferencia bancaria"
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="es">
|
|
||||||
<head>
|
|
||||||
<title>{{_.subject}}</title>
|
|
||||||
<meta charset="utf8"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="container">
|
|
||||||
<!-- Header block -->
|
|
||||||
{{$.header}}
|
|
||||||
<!-- Header block end -->
|
|
||||||
|
|
||||||
<!-- Title block -->
|
|
||||||
<div class="title">
|
|
||||||
<h1>{{_.title}}</h1>
|
|
||||||
</div>
|
|
||||||
<!-- Title block end -->
|
|
||||||
|
|
||||||
<!-- Mail body block -->
|
|
||||||
<div class="body">
|
|
||||||
<p>{{_.hello}} <strong>#{{alias}}</strong></p>
|
|
||||||
<p>{{message}}</p>
|
|
||||||
</div>
|
|
||||||
<!-- Mail body block end -->
|
|
||||||
|
|
||||||
<!-- Footer block -->
|
|
||||||
{{$.footer}}
|
|
||||||
<!-- Footer block end -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"subject": "Has recibido una nueva notificación",
|
|
||||||
"title": "Nueva notificación",
|
|
||||||
"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."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
var path = require('path');
|
|
||||||
var database = require(path.join(__dirname, '../../database.js'));
|
|
||||||
var locale = require(path.join(__dirname, '../../locale.js'));
|
|
||||||
|
|
||||||
module.exports = class NotificationAlias {
|
|
||||||
getData(params, cb) {
|
|
||||||
this.params = params;
|
|
||||||
|
|
||||||
let query = `SELECT alias, CONCAT(alias, '@verdnatura.es') AS recipient
|
|
||||||
FROM account.mailAlias
|
|
||||||
WHERE alias = ?`;
|
|
||||||
|
|
||||||
database.pool.query(query, [params.alias], (error, result) => {
|
|
||||||
if (error || result.length == 0)
|
|
||||||
return cb(new Error('No template data found'));
|
|
||||||
|
|
||||||
Object.assign(this, result[0]);
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get subject() {
|
|
||||||
return this._.notificationCode[this.params.code].subject;
|
|
||||||
}
|
|
||||||
|
|
||||||
get message() {
|
|
||||||
return locale.parseText(this._.notificationCode[this.params.code].message, this.params.bodyParams);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
[]
|
|
|
@ -1,76 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="es">
|
|
||||||
<head>
|
|
||||||
<title>{{_.subject}}</title>
|
|
||||||
<meta charset="utf8"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div style="width: 600px;margin: 0 auto;font-family: arial, sans-serif;font-size: 16px;color: #555">
|
|
||||||
<!-- Banner block -->
|
|
||||||
<div>
|
|
||||||
<a href="https://www.verdnatura.es"/><img src="cid:header.png" alt="VerdNatura" style="margin:0"/></a>
|
|
||||||
</div>
|
|
||||||
<!-- Banner block end -->
|
|
||||||
|
|
||||||
<!-- Title block -->
|
|
||||||
<div style="padding: 35px 0;background-color:#95d831;text-align: center">
|
|
||||||
<h1 style="margin: 0;font-size: 32px;color: #333;">{{_.title}}</h1>
|
|
||||||
</div>
|
|
||||||
<!-- Title block end -->
|
|
||||||
|
|
||||||
<!-- Mail body block -->
|
|
||||||
<div style="padding: 20px 0">
|
|
||||||
<p style="text-align: justify">Hola, {{recipientName}}</p>
|
|
||||||
<p style="text-align: justify">{{_.bodyDescription}} <strong>{{senderName}}</strong>:</p>
|
|
||||||
<p style="text-align: justify;font-size: 22px">"{{message}}"<p>
|
|
||||||
<p style="text-align: justify">{{_.noticeDescription}} <strong>'{{categoryName}}'</strong>. {{_.unsubscribe}}</p>
|
|
||||||
</div>
|
|
||||||
<!-- Mail body block end -->
|
|
||||||
|
|
||||||
<!-- Action button block -->
|
|
||||||
<div style="background-color: #333;overflow:hidden">
|
|
||||||
<a href="https://www.verdnatura.es" target="_blank" style="display:block;float:left;width:300px;height:72px;color:#fff;font-size:22px;text-decoration:none">
|
|
||||||
<div style="float:left;width:230px;padding:22px 0;height:72px;text-align:center">{{_.actionButton}}</div>
|
|
||||||
<div style="background-color:#95d831;text-align:center;float:right;padding-top:22px;height:50px;width:70px"><img style="margin:0" src="cid:action.png"/></div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="https://goo.gl/forms/j8WSL151ZW6QtlT72" target="_blank" style="display:block;float:left;width:300px;height:72px;color:#fff;font-size:22px;text-decoration:none">
|
|
||||||
<div style="float:left;width:230px;padding:22px 0;height:72px;text-align:center">{{_.infoButton}}</div>
|
|
||||||
<div style="background-color:#95d831;text-align:center;float:right;padding-top:22px;height:50px;width:70px"><img style="margin:0" src="cid:info.png"/></div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<!-- Action button block end-->
|
|
||||||
|
|
||||||
<!-- Networks block -->
|
|
||||||
<div style="padding:20px 0;background-color:#555;text-align:center">
|
|
||||||
<a href="https://www.facebook.com/Verdnatura" target="_blank" style="text-decoration:none;margin-right: 10px">
|
|
||||||
<img src="cid:facebook.png" alt="Visita nuestro Facebook" style="margin:0"/>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.twitter.com/Verdnatura" target="_blank" style="text-decoration:none;margin-right: 10px">
|
|
||||||
<img src="cid:twitter.png" alt="Visita nuestro Twitter" style="margin:0"/>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.youtube.com/Verdnatura" target="_blank" style="text-decoration:none;margin-right: 10px">
|
|
||||||
<img src="cid:youtube.png" alt="Visita nuestro canal de Youtube" style="margin:0"/>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.pinterest.com/Verdnatura" target="_blank" style="text-decoration:none;margin-right: 10px">
|
|
||||||
<img src="cid:pinterest.png" alt="Visita nuestro Pinterest" style="margin:0"/>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.instagram.com/Verdnatura" target="_blank" style="text-decoration:none;margin-right: 10px">
|
|
||||||
<img src="cid:instagram.png" alt="Visita nuestro Instagram" style="margin:0"/>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.linkedin.com/company/verdnatura" target="_blank" style="text-decoration:none;margin-right: 10px">
|
|
||||||
<img src="cid:linkedin.png" alt="Visita nuestro Linkedin" style="width:50px;margin:0"/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<!-- Networks block end -->
|
|
||||||
|
|
||||||
<!-- Privacy block -->
|
|
||||||
<div style="padding:20px 0;font-size:10px;font-weight:100">
|
|
||||||
<p style="text-align: justify">{{_.fiscalAddress}}</p>
|
|
||||||
<p style="text-align: justify">{{_.privacy}}</p>
|
|
||||||
<p style="text-align: justify">{{_.privacyLaw}}</p>
|
|
||||||
</div>
|
|
||||||
<!-- Privacy block end -->
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"subject": "Has recibido una nueva notificación",
|
|
||||||
"title": "Nueva notificación",
|
|
||||||
"hello": "Hola",
|
|
||||||
"bodyDescription": "Has recibido la siguiente notificación de",
|
|
||||||
"noticeDescription": "Recibes esta notificación porque estás suscrito a",
|
|
||||||
"unsubscribe": "Puedes dejar de recibir estas notificaciones desde 'Notificaciones > Configuración' en cualquier aplicación de VerdNatura.",
|
|
||||||
"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."
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
var path = require('path');
|
|
||||||
var database = require(path.join(__dirname, '../../database.js'));
|
|
||||||
|
|
||||||
module.exports = class NotificationNotice {
|
|
||||||
getData(params, cb) {
|
|
||||||
let query = `SELECT
|
|
||||||
LOWER(ct.code) countryCode,
|
|
||||||
c.email recipient,
|
|
||||||
nc.name categoryName,
|
|
||||||
recipient.name recipientName,
|
|
||||||
sender.name senderName
|
|
||||||
FROM client c
|
|
||||||
JOIN account.user recipient ON recipient.id = c.id
|
|
||||||
JOIN country ct ON ct.id = c.countryFk
|
|
||||||
JOIN noticeCategory nc ON nc.keyName = ?
|
|
||||||
JOIN account.user sender ON sender.id = ?
|
|
||||||
WHERE c.id = ?`;
|
|
||||||
database.pool.query(query, [params.category, params.sender, params.recipient], (error, result) => {
|
|
||||||
if (error || result.length == 0)
|
|
||||||
return cb({status: 'REJECT', data: {message: 'No data found', error: error}});
|
|
||||||
|
|
||||||
Object.assign(this, result[0]);
|
|
||||||
this.message = params.message;
|
|
||||||
|
|
||||||
cb({status: 'ACCEPT', data: {}});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
[]
|
|
|
@ -1,39 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="es">
|
|
||||||
<head>
|
|
||||||
<title>{{_.subject}}</title>
|
|
||||||
<meta charset="utf8"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="container">
|
|
||||||
<!-- Header block -->
|
|
||||||
{{$.header}}
|
|
||||||
<!-- Header block end -->
|
|
||||||
|
|
||||||
<!-- Title block -->
|
|
||||||
<div class="title">
|
|
||||||
<h1>{{_.title}}</h1>
|
|
||||||
</div>
|
|
||||||
<!-- Title block end -->
|
|
||||||
|
|
||||||
<!-- Mail body block -->
|
|
||||||
<div class="body">
|
|
||||||
<p>{{_.dear}}</p>
|
|
||||||
<p>{{_.bodyDescription}}</p>
|
|
||||||
<p>
|
|
||||||
<div>{{_.paymentMethod}} <strong>{{payMethodName}}</strong></div>
|
|
||||||
{{{paymentDay}}}
|
|
||||||
</p>
|
|
||||||
<p>{{paymentAdvice}}</p>
|
|
||||||
<p>{{_.notifyError}}</p>
|
|
||||||
</div>
|
|
||||||
<!-- Mail body block end -->
|
|
||||||
|
|
||||||
<!-- Footer block -->
|
|
||||||
{{$.footer}}
|
|
||||||
<!-- Footer block end -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"subject": "Cambios en las condiciones de pago",
|
|
||||||
"title": "Cambio en las condiciones",
|
|
||||||
"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:",
|
|
||||||
"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."
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"subject": "Changement des C.G.V",
|
|
||||||
"title": "Changement des C.G.V",
|
|
||||||
"dear": "Chèr client,",
|
|
||||||
"bodyDescription": "Nous vous informons que les conditions de paiement ont changé. Voici les nouvelles conditions:",
|
|
||||||
"paymentMethod": "Méthode de paiement:",
|
|
||||||
"paymentDay": "Date paiement:",
|
|
||||||
"everyMonth": "de chaque mois",
|
|
||||||
"cardPaymentAdvice": "Avec votre mode de règlement vous devrez payer le montant des commandes avant son départ.",
|
|
||||||
"accountPaymentAdviceBefore": "Avec ce mode de règlement nous vous passerons un prélèvement automatique sur votre compte bancaire",
|
|
||||||
"accountPaymentAdviceAfter": "pour le montant dû, au date à terme établi en nos conditions.",
|
|
||||||
"notifyError": "Pour tout renseignement contactez votre commercial.."
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
var path = require('path');
|
|
||||||
var database = require(path.join(__dirname, '../../database.js'));
|
|
||||||
var format = require(path.join(__dirname, '../../util/format.js'));
|
|
||||||
|
|
||||||
module.exports = class PaymentUpdate {
|
|
||||||
getData(params, cb) {
|
|
||||||
let query = `SELECT
|
|
||||||
pm.id payMethodFk,
|
|
||||||
pm.name payMethodName,
|
|
||||||
c.dueDay,
|
|
||||||
c.iban,
|
|
||||||
LOWER(ct.code) countryCode,
|
|
||||||
c.email recipient
|
|
||||||
FROM client c
|
|
||||||
JOIN payMethod pm ON pm.id = c.payMethodFk
|
|
||||||
JOIN country ct ON ct.id = c.countryFk
|
|
||||||
WHERE c.id = ?`;
|
|
||||||
|
|
||||||
this.clientId = params.clientId;
|
|
||||||
|
|
||||||
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 paymentDay() {
|
|
||||||
if (this.payMethodFk != 5)
|
|
||||||
return `<div>${this._.paymentDay} <strong>${this.dueDay} ${this._.everyMonth}</strong></div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
get paymentAdvice() {
|
|
||||||
switch (this.payMethodFk) {
|
|
||||||
case 4:
|
|
||||||
return `${this._.accountPaymentAdviceBefore} ${format.partialAccountAddress(this.iban)} ${this._.accountPaymentAdviceAfter}`;
|
|
||||||
case 5:
|
|
||||||
return this._.cardPaymentAdvice;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
["model.ezp"]
|
|
|
@ -1,68 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="es">
|
|
||||||
<head>
|
|
||||||
<title>{{_.subject}}</title>
|
|
||||||
<meta charset="utf8"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="container">
|
|
||||||
<!-- Header block -->
|
|
||||||
{{$.header}}
|
|
||||||
<!-- Header block end -->
|
|
||||||
|
|
||||||
<!-- Title block -->
|
|
||||||
<div class="title">
|
|
||||||
<h1>{{_.title}}</h1>
|
|
||||||
</div>
|
|
||||||
<!-- Title block end -->
|
|
||||||
|
|
||||||
<!-- Mail body block -->
|
|
||||||
<div class="body">
|
|
||||||
<p>{{_.dear}}</p>
|
|
||||||
<p>{{_.bodyDescription}}</p>
|
|
||||||
<p>{{{_.followGuide}}}</p>
|
|
||||||
<p>{{{_.downloadFrom}}}</p>
|
|
||||||
|
|
||||||
<h1>{{_.sectionQLabelTitle}}</h1>
|
|
||||||
|
|
||||||
<p>{{_.sectionQLabelDescription}}</p>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li>{{_.sectionQLabelStep1}}</li>
|
|
||||||
<li>{{_.sectionQLabelStep2}}</li>
|
|
||||||
<li>{{_.sectionQLabelStep3}}</li>
|
|
||||||
<li>{{{_.sectionQLabelStep4}}}</li>
|
|
||||||
<li>{{_.sectionQLabelStep5}}</li>
|
|
||||||
<li>{{_.sectionQLabelStep6}}</li>
|
|
||||||
<li>{{_.sectionQLabelStep7}}</li>
|
|
||||||
<li>{{_.sectionQLabelStep8}}</li>
|
|
||||||
<li>{{{_.sectionQLabelStep9}}}</li>
|
|
||||||
<li>{{_.sectionQLabelStep10}}</li>
|
|
||||||
<li>{{_.sectionQLabelStep11}}</li>
|
|
||||||
<li>{{_.sectionQLabelStep12}}</li>
|
|
||||||
<li>{{_.sectionQLabelStep13}}</li>
|
|
||||||
<li>{{_.sectionQLabelStep14}}</li>
|
|
||||||
<li>{{_.sectionQLabelStep15}}</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<h1>{{_.sectionHelpTitle}}</h1>
|
|
||||||
|
|
||||||
<p >{{_.sectionHelpDescription}}</p>
|
|
||||||
<p>{{{_.sectionHelpDownloadRemoteSupport}}}</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{{{salesPersonName}}}
|
|
||||||
{{{salesPersonPhone}}}
|
|
||||||
{{{salesPersonEmail}}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<!-- Mail body block end -->
|
|
||||||
|
|
||||||
<!-- Footer block -->
|
|
||||||
{{$.footer}}
|
|
||||||
<!-- Footer block end -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"subject": "Instalación y configuración de impresora",
|
|
||||||
"title": "¡GRACIAS POR SU CONFIANZA!",
|
|
||||||
"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 <a href=\"https:\//www.youtube.com/watch?v=qhb0kgQF3o8\" title=\"Youtube\" target=\"_blank\" style=\"color:#8dba25\">https://www.youtube.com/watch?v=qhb0kgQF3o8</a>. También necesitará el QLabel, el programa para imprimir las cintas.",
|
|
||||||
"downloadFrom": "Puede descargarlo desde este enlace <a href=\"http://ww.godexintl.com/es1/download/downloads/Download/2996\" title=\"Descargar QLabel\" target=\"_blank\" style=\"color:#8dba25\">http://ww.godexintl.com/es1/download/downloads/Download/2996</a>",
|
|
||||||
"sectionQLabelTitle": "Utilización de QLabel",
|
|
||||||
"sectionQLabelDescription": "Para utilizar el programa de impresión de cintas siga estos pasos:",
|
|
||||||
"sectionQLabelStep1": "Abra el programa QLabel.",
|
|
||||||
"sectionQLabelStep2": "Haga clic en el icono de la barra superior superior de la \"carpeta\".",
|
|
||||||
"sectionQLabelStep3": "Seleccione el archivo plantilla llamado \"model.ezp\".",
|
|
||||||
"sectionQLabelStep4": "Haga clic <strong>encima del texto</strong> con el boton secundario del ratón.",
|
|
||||||
"sectionQLabelStep5": "Elija la primera opcion \"setup\".",
|
|
||||||
"sectionQLabelStep6": "Cambie el texto para imprimir.",
|
|
||||||
"sectionQLabelStep7": "Haga clic en el boton \"Ok\".",
|
|
||||||
"sectionQLabelStep8": "Desplácese con el raton para ver la medida máxima.",
|
|
||||||
"sectionQLabelStep9": "Haga clic <strong>encima del texto</strong> con el botón secundario del ratón.",
|
|
||||||
"sectionQLabelStep10": "Elija la primera opcion \"Setup printer\".",
|
|
||||||
"sectionQLabelStep11": "Haga clic en la primera pestalla \"Label Setup\".",
|
|
||||||
"sectionQLabelStep12": "Modifique la propidad \"Paper Height\".",
|
|
||||||
"sectionQLabelStep13": "Haga clic en el boton \"Ok\".",
|
|
||||||
"sectionQLabelStep14": "Haga clic sobre el icono de la impresora.",
|
|
||||||
"sectionQLabelStep15": "Haga clic en \"Print\".",
|
|
||||||
"sectionHelpTitle": "¿Necesita ayuda?",
|
|
||||||
"sectionHelpDescription": "Si necesita ayuda, descárguese nuestro programa de soporte para poder conectarnos remotamente a su equipo y hacerle la instalación. Proporciónenos un horario de contacto para atenderle, y contactaremos con usted.",
|
|
||||||
"sectionHelpDownloadRemoteSupport": "Puede descargarse el programa desde este enlace <a href=\"http://soporte.verdnatura.es\" title=\"Soporte Verdnatura\" target=\"_blank\" style=\"color:#8dba25\">http://soporte.verdnatura.es</a>.",
|
|
||||||
"salesPersonNameText": "Soy su comercial y mi nombre es",
|
|
||||||
"salesPersonPhoneText": "Teléfono y whatsapp",
|
|
||||||
"salesPersonEmailText": "Dirección de e-mail"
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
var path = require('path');
|
|
||||||
var database = require(path.join(__dirname, '../../database.js'));
|
|
||||||
var format = require(path.join(__dirname, '../../util/format.js'));
|
|
||||||
|
|
||||||
module.exports = class PrinterSetup {
|
|
||||||
getData(params, cb) {
|
|
||||||
let query = `SELECT
|
|
||||||
CONCAT(w.name, ' ', w.firstName) name,
|
|
||||||
w.phone AS phone,
|
|
||||||
CONCAT(u.name, '@verdnatura.es') AS email,
|
|
||||||
LOWER(ct.code) countryCode,
|
|
||||||
c.email recipient
|
|
||||||
FROM client c
|
|
||||||
LEFT JOIN worker w ON w.id = c.salesPersonFk
|
|
||||||
LEFT JOIN account.user u ON u.id = w.userFk
|
|
||||||
JOIN country ct ON ct.id = c.countryFk
|
|
||||||
WHERE c.id = ?`;
|
|
||||||
|
|
||||||
this.clientId = params.clientId;
|
|
||||||
this.isPreview = params.isPreview;
|
|
||||||
|
|
||||||
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 `<div>${this._.salesPersonNameText}: <strong>${this.name}</strong></div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
get salesPersonPhone() {
|
|
||||||
if (this.phone)
|
|
||||||
return `<div>${this._.salesPersonPhoneText}: <strong>${format.phone(this.phone)}</strong></div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
get salesPersonEmail() {
|
|
||||||
if (this.email)
|
|
||||||
return `<div>${this._.salesPersonEmailText}: ` +
|
|
||||||
`<strong><a href="mailto:${this.email}" target="_blank" style="color:#8dba25">${this.email}</strong></div>`;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
[]
|
|
|
@ -1,54 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="es">
|
|
||||||
<head>
|
|
||||||
<title>{{_.subject}}</title>
|
|
||||||
<meta charset="utf8"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="container">
|
|
||||||
<!-- Header block -->
|
|
||||||
{{$.header}}
|
|
||||||
<!-- Header block end -->
|
|
||||||
|
|
||||||
<!-- Title block -->
|
|
||||||
<div class="title">
|
|
||||||
<h1>{{_.title}}</h1>
|
|
||||||
</div>
|
|
||||||
<!-- Title block end -->
|
|
||||||
|
|
||||||
<!-- Mail body block -->
|
|
||||||
<div class="body">
|
|
||||||
<p>{{_.dear}}</p>
|
|
||||||
<p>{{_.bodyDescription}}</p>
|
|
||||||
<p>{{_.conclusion}}</p>
|
|
||||||
|
|
||||||
{{#isPreview}}
|
|
||||||
<a href="/print/manuscript/sepa-core/{{companyId}}/{{clientId}}/preview?token={{token}}" target="_blank" title="Ver adjunto">
|
|
||||||
<div class="attachment">
|
|
||||||
<div class="attachment-icon">
|
|
||||||
<img src="cid:default/preview.svg" alt="Ver adjunto"/>
|
|
||||||
</div>
|
|
||||||
<span>Ver adjunto</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/print/manuscript/sepa-core/{{companyId}}/{{clientId}}?token={{token}}" target="_blank" title="Descargar adjunto">
|
|
||||||
<div class="attachment">
|
|
||||||
<div class="attachment-icon">
|
|
||||||
<img src="cid:default/download.svg" alt="Descargar adjunto"/>
|
|
||||||
</div>
|
|
||||||
<span>Descargar PDF</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
{{/isPreview}}
|
|
||||||
</div>
|
|
||||||
<!-- Mail body block end -->
|
|
||||||
|
|
||||||
<!-- Footer block -->
|
|
||||||
{{$.footer}}
|
|
||||||
<!-- Footer block end -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
"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."
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
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
|
|
||||||
CONCAT(w.name, ' ', w.firstName) name,
|
|
||||||
w.phone AS phone,
|
|
||||||
CONCAT(u.name, '@verdnatura.es') AS email,
|
|
||||||
LOWER(ct.code) countryCode,
|
|
||||||
c.email recipient
|
|
||||||
FROM client c
|
|
||||||
LEFT JOIN worker w ON w.id = c.salesPersonFk
|
|
||||||
LEFT JOIN account.user u ON u.id = w.userFk
|
|
||||||
JOIN country ct ON ct.id = c.countryFk
|
|
||||||
WHERE c.id = ?`;
|
|
||||||
|
|
||||||
this.clientId = params.clientId;
|
|
||||||
this.companyId = params.companyId;
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,41 +0,0 @@
|
||||||
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) {
|
|
||||||
if (!addressNumber) return;
|
|
||||||
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) {
|
|
||||||
if (!addressNumber) return;
|
|
||||||
let address = this.accountAddress(addressNumber);
|
|
||||||
return address.substring(0, 19).replace(/[0-9]/g, 'X') + address.substring(19, 24);
|
|
||||||
},
|
|
||||||
|
|
||||||
phone: function(number) {
|
|
||||||
return number;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,10 +0,0 @@
|
||||||
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];
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,23 +0,0 @@
|
||||||
{
|
|
||||||
"name": "vn-mailer",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"description": "Servidor de envío de correos",
|
|
||||||
"main": "server/server.js",
|
|
||||||
"dependencies": {
|
|
||||||
"body-parser": "^1.17.2",
|
|
||||||
"express": "^4.15.3",
|
|
||||||
"inline-css": "^2.2.2",
|
|
||||||
"mustache": "^2.3.0",
|
|
||||||
"mysql": "^2.13.0",
|
|
||||||
"nodemailer": "^4.0.1",
|
|
||||||
"path": "^0.12.7",
|
|
||||||
"request": "^2.83.0",
|
|
||||||
"require-yaml": "0.0.1",
|
|
||||||
"fs-extra": "^5.0.0"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://git.verdnatura.es/salix"
|
|
||||||
},
|
|
||||||
"license": "GPL-3.0"
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
var express = require('express');
|
|
||||||
var app = module.exports = express();
|
|
||||||
var bodyParser = require('body-parser');
|
|
||||||
var config = require('../application/config.js');
|
|
||||||
var mail = require('../application/mail.js');
|
|
||||||
var database = require('../application/database.js');
|
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
// Body parser middleware
|
|
||||||
app.use(bodyParser.json());
|
|
||||||
app.use(bodyParser.urlencoded({extended: true}));
|
|
||||||
|
|
||||||
app.use('/static', express.static(path.join(__dirname, '../static')));
|
|
||||||
|
|
||||||
// Load routes
|
|
||||||
app.use('/', require('../application/router.js'));
|
|
||||||
|
|
||||||
app.start = function(port) {
|
|
||||||
var listener = app.listen(port ? port : config.app.port, function() {
|
|
||||||
var servicePath = 'http://' + listener.address().address + ':' + listener.address().port;
|
|
||||||
mail.init();
|
|
||||||
database.init();
|
|
||||||
|
|
||||||
if (config.app.debug) {
|
|
||||||
let packageJson = require('../package.json');
|
|
||||||
console.log(`Web server ${packageJson.name} listening at: ${servicePath}`);
|
|
||||||
console.log(`${packageJson.name} service debug mode enabled`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return listener;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (require.main === module) {
|
|
||||||
app.start();
|
|
||||||
}
|
|
|
@ -1,217 +0,0 @@
|
||||||
body {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
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: 11px;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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%
|
|
||||||
}
|
|
||||||
|
|
||||||
.attachment {
|
|
||||||
overflow: hidden;
|
|
||||||
margin-top: 10px
|
|
||||||
}
|
|
||||||
|
|
||||||
.attachment-icon {
|
|
||||||
float: left
|
|
||||||
}
|
|
||||||
|
|
||||||
.attachment span {
|
|
||||||
padding: 16px 0 0 10px;
|
|
||||||
float: left
|
|
||||||
}
|
|
||||||
|
|
||||||
.columns {
|
|
||||||
overflow: hidden
|
|
||||||
}
|
|
||||||
|
|
||||||
.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
|
|
||||||
}
|
|
|
@ -31,9 +31,6 @@ http {
|
||||||
}
|
}
|
||||||
{{/services}}
|
{{/services}}
|
||||||
|
|
||||||
location ~ ^/mailer(?:/(.*)?)$ {
|
|
||||||
proxy_pass http://127.0.0.1:{{defaultPort}}/$1$is_args$args;
|
|
||||||
}
|
|
||||||
location ~ ^/static(?:/(.*)?)$ {
|
location ~ ^/static(?:/(.*)?)$ {
|
||||||
proxy_pass http://127.0.0.1:{{devServerPort}}/$1$is_args$args;
|
proxy_pass http://127.0.0.1:{{devServerPort}}/$1$is_args$args;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,6 @@ server {
|
||||||
}
|
}
|
||||||
{{/services}}
|
{{/services}}
|
||||||
|
|
||||||
location ~ ^/mailer(?:/(.*))?$ {
|
|
||||||
resolver 127.0.0.11;
|
|
||||||
proxy_pass http://mailer:{{defaultPort}}/$1$is_args$args;
|
|
||||||
}
|
|
||||||
location ~ ^/static(?:/(.*))?$ {
|
location ~ ^/static(?:/(.*))?$ {
|
||||||
alias /salix/dist/$1;
|
alias /salix/dist/$1;
|
||||||
autoindex on;
|
autoindex on;
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
node_modules
|
|
|
@ -1,5 +0,0 @@
|
||||||
FROM vn-loopback:latest
|
|
||||||
|
|
||||||
COPY print /app
|
|
||||||
WORKDIR /app
|
|
||||||
CMD ["pm2-docker", "./server/server.js"]
|
|
|
@ -1,80 +0,0 @@
|
||||||
var database = require('./database.js');
|
|
||||||
let config = require('./config.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: async function() {
|
|
||||||
let query = 'SELECT userId, ttl, created FROM salix.AccessToken WHERE id = ?';
|
|
||||||
|
|
||||||
try {
|
|
||||||
let [result] = await database.pool.query(query, [this.getToken()]);
|
|
||||||
let token = result[0];
|
|
||||||
|
|
||||||
if (result.length == 0)
|
|
||||||
throw new Error('Invalid token');
|
|
||||||
|
|
||||||
if (this.isTokenExpired(token.created, token.ttl))
|
|
||||||
throw new Error('Token expired');
|
|
||||||
|
|
||||||
// Set proxy host
|
|
||||||
let proxy = config.proxy;
|
|
||||||
|
|
||||||
if (!proxy)
|
|
||||||
proxy = {
|
|
||||||
host: 'localhost',
|
|
||||||
port: 80
|
|
||||||
};
|
|
||||||
|
|
||||||
this.request.proxyHost = `http://${proxy.host}:${proxy.port}`;
|
|
||||||
this.request.user = {
|
|
||||||
id: token.userId,
|
|
||||||
token: this.getToken()
|
|
||||||
};
|
|
||||||
this.next();
|
|
||||||
} catch (e) {
|
|
||||||
this.response.status(401).send({message: e.message});
|
|
||||||
this.next(e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,30 +0,0 @@
|
||||||
require('require-yaml');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const package = require('../package.json');
|
|
||||||
let configPath = `/config/${package.name}`;
|
|
||||||
let nginxConfigPath = '/config/nginx';
|
|
||||||
|
|
||||||
let env = process.env.NODE_ENV ? process.env.NODE_ENV : 'development';
|
|
||||||
|
|
||||||
if (env === 'development') {
|
|
||||||
configPath = `${__dirname}/config`;
|
|
||||||
nginxConfigPath = '../../nginx';
|
|
||||||
}
|
|
||||||
|
|
||||||
let config = require(`${configPath}/datasources.json`);
|
|
||||||
let configEnvFile = `${configPath}/datasources.${env}.json`;
|
|
||||||
|
|
||||||
if (fs.existsSync(configEnvFile))
|
|
||||||
Object.assign(config, require(configEnvFile));
|
|
||||||
|
|
||||||
let proxyConf = require(`${nginxConfigPath}/config.yml`);
|
|
||||||
let proxyEnvFile = `${nginxConfigPath}/config.${env}.yml`;
|
|
||||||
|
|
||||||
if (fs.existsSync(proxyEnvFile))
|
|
||||||
Object.assign(proxyConf, require(proxyEnvFile));
|
|
||||||
|
|
||||||
config.proxy = proxyConf;
|
|
||||||
config.package = package;
|
|
||||||
config.env = env;
|
|
||||||
|
|
||||||
module.exports = config;
|
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"app": {
|
|
||||||
"port": 3000,
|
|
||||||
"debug": false,
|
|
||||||
"defaultLanguage": "es"
|
|
||||||
},
|
|
||||||
"mysql": {
|
|
||||||
"host": "localhost",
|
|
||||||
"port": 3306,
|
|
||||||
"database": "vn",
|
|
||||||
"user": "root",
|
|
||||||
"password": "root"
|
|
||||||
},
|
|
||||||
"pdf": {
|
|
||||||
"format": "A4",
|
|
||||||
"border": "1.5cm"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
var mysql = require('mysql2/promise');
|
|
||||||
let config = require('./config.js');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
/**
|
|
||||||
* Pool instance
|
|
||||||
*/
|
|
||||||
pool: null,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start database pool
|
|
||||||
*/
|
|
||||||
init: function() {
|
|
||||||
this.pool = mysql.createPool(config.mysql);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,42 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,171 +0,0 @@
|
||||||
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');
|
|
||||||
var auth = require('../auth.js');
|
|
||||||
|
|
||||||
// Auth middleware
|
|
||||||
var requestToken = function(request, response, next) {
|
|
||||||
auth.init(request, response, next);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sepa core
|
|
||||||
router.get('/sepa-core/:companyId/:clientId', requestToken, function(request, response, next) {
|
|
||||||
let params = {
|
|
||||||
clientId: request.params.clientId,
|
|
||||||
companyId: request.params.companyId
|
|
||||||
};
|
|
||||||
|
|
||||||
template.get('sepa-core', params, (error, result) => {
|
|
||||||
if (error)
|
|
||||||
return response.status(400).json({message: error.message});
|
|
||||||
|
|
||||||
pdf.create(result.body, config.pdf).toStream(function(error, stream) {
|
|
||||||
if (error)
|
|
||||||
throw Error(error);
|
|
||||||
|
|
||||||
response.setHeader('Content-Disposition', 'attachment; filename="sepa-core.pdf"');
|
|
||||||
response.setHeader('Content-type', 'application/pdf');
|
|
||||||
stream.pipe(response);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sepa core html preview
|
|
||||||
router.get('/sepa-core/:companyId/:clientId/preview', requestToken, function(request, response, next) {
|
|
||||||
let params = {
|
|
||||||
clientId: request.params.clientId,
|
|
||||||
companyId: request.params.companyId,
|
|
||||||
isPreview: true
|
|
||||||
};
|
|
||||||
|
|
||||||
template.get('sepa-core', params, (error, result) => {
|
|
||||||
if (error)
|
|
||||||
return response.status(400).json({message: error.message});
|
|
||||||
|
|
||||||
response.send(result.body);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Debtor letter
|
|
||||||
router.get('/letter-debtor/:companyId/:clientId', requestToken, function(request, response, next) {
|
|
||||||
let params = {
|
|
||||||
clientId: request.params.clientId,
|
|
||||||
companyId: request.params.companyId
|
|
||||||
};
|
|
||||||
|
|
||||||
template.get('letter-debtor', params, (error, result) => {
|
|
||||||
if (error)
|
|
||||||
return response.status(400).json({message: error.message});
|
|
||||||
|
|
||||||
pdf.create(result.body, config.pdf).toStream(function(error, stream) {
|
|
||||||
if (error)
|
|
||||||
throw Error(error);
|
|
||||||
|
|
||||||
response.setHeader('Content-Disposition', 'attachment; filename="extracto.pdf"');
|
|
||||||
response.setHeader('Content-type', 'application/pdf');
|
|
||||||
stream.pipe(response);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Debtor letter html preview
|
|
||||||
router.get('/letter-debtor/:companyId/:clientId/preview', requestToken, function(request, response, next) {
|
|
||||||
let params = {
|
|
||||||
clientId: request.params.clientId,
|
|
||||||
companyId: request.params.companyId,
|
|
||||||
isPreview: true
|
|
||||||
};
|
|
||||||
|
|
||||||
template.get('letter-debtor', params, (error, result) => {
|
|
||||||
if (error)
|
|
||||||
return response.status(400).json({message: error.message});
|
|
||||||
|
|
||||||
response.send(result.body);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delivery note
|
|
||||||
router.get('/delivery-note/:ticketId/preview', requestToken, function(request, response) {
|
|
||||||
let params = {
|
|
||||||
ticketId: request.params.ticketId,
|
|
||||||
isPreview: true
|
|
||||||
};
|
|
||||||
|
|
||||||
template.get('delivery-note', params, (error, result) => {
|
|
||||||
if (error)
|
|
||||||
return response.status(400).json(error);
|
|
||||||
|
|
||||||
response.send(result.body);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/delivery-note/:ticketId', requestToken, function(request, response, next) {
|
|
||||||
let params = {
|
|
||||||
ticketId: request.params.ticketId
|
|
||||||
};
|
|
||||||
|
|
||||||
template.get('delivery-note', params, (error, result) => {
|
|
||||||
if (error)
|
|
||||||
return response.status(400).json({message: error.message});
|
|
||||||
|
|
||||||
pdf.create(result.body, config.pdf).toStream(function(error, stream) {
|
|
||||||
if (error)
|
|
||||||
throw Error(error);
|
|
||||||
|
|
||||||
stream.pipe(response);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/claim-pickup/:claimId/preview', requestToken, function(request, response) {
|
|
||||||
let params = {
|
|
||||||
claimId: request.params.claimId,
|
|
||||||
isPreview: true
|
|
||||||
};
|
|
||||||
|
|
||||||
template.get('claim-pickup', params, (error, result) => {
|
|
||||||
if (error)
|
|
||||||
return response.status(400).json(error);
|
|
||||||
|
|
||||||
response.send(result.body);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/claim-pickup/:claimId', requestToken, function(request, response) {
|
|
||||||
let params = {
|
|
||||||
claimId: request.params.claimId
|
|
||||||
};
|
|
||||||
|
|
||||||
template.get('claim-pickup', params, (error, result) => {
|
|
||||||
if (error)
|
|
||||||
return response.status(400).json({message: error.message});
|
|
||||||
|
|
||||||
pdf.create(result.body, config.pdf).toStream(function(error, stream) {
|
|
||||||
if (error)
|
|
||||||
throw Error(error);
|
|
||||||
stream.pipe(response);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
*/
|
|
|
@ -1,31 +0,0 @@
|
||||||
var express = require('express');
|
|
||||||
var router = new express.Router();
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
// Default page
|
|
||||||
router.get('/', function(request, response) {
|
|
||||||
response.json({});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Manuscripts
|
|
||||||
router.use('/manuscript', require('./route/manuscript.js'));
|
|
||||||
|
|
||||||
// Serve static images
|
|
||||||
router.use('/static/:template/:image', function(request, response) {
|
|
||||||
let imagePath = path.join(__dirname, '/template/', request.params.template, '/image/', request.params.image);
|
|
||||||
|
|
||||||
fs.stat(imagePath, function(error) {
|
|
||||||
if (error)
|
|
||||||
return response.json({message: 'Image not found'});
|
|
||||||
|
|
||||||
let readStream = fs.createReadStream(imagePath);
|
|
||||||
|
|
||||||
readStream.on('open', function() {
|
|
||||||
readStream.pipe(response);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
|
|
@ -1,256 +0,0 @@
|
||||||
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}`, '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('<title>(.*?)</title>', 'i'));
|
|
||||||
|
|
||||||
if (title)
|
|
||||||
params.subject = title[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.renderImages(template, body, params.isPreview, (error, body) => {
|
|
||||||
if (error)
|
|
||||||
return cb(error);
|
|
||||||
|
|
||||||
cb(null, {body: body});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let getDataCb = () => {
|
|
||||||
this.render(templatePath, params, 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;
|
|
||||||
instance.isPreview = params.isPreview;
|
|
||||||
getDataCb(null, result);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render template
|
|
||||||
* @param {String} path - Template path
|
|
||||||
* @param {Object} data - Params
|
|
||||||
* @param {Object} cb - Callback
|
|
||||||
*/
|
|
||||||
render: function(path, params, 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, params, regexp, cb) {
|
|
||||||
let index = 1;
|
|
||||||
|
|
||||||
subTpl.forEach(keyName => {
|
|
||||||
subTplName = keyName.replace(regexp, '$1');
|
|
||||||
|
|
||||||
this.get(subTplName, params, (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', 'common.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 = '<style>' + comCss + css + '</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, isPreview, 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 src = tplImages[i].replace('src="cid:', '').replace('"', '').split('/');
|
|
||||||
let attachmentTpl = src[0];
|
|
||||||
let attachment = src[1];
|
|
||||||
|
|
||||||
if (isPreview) {
|
|
||||||
let imagePath = `/print/static/${attachmentTpl}/${attachment}`;
|
|
||||||
body = body.replace(tplImages[i], `src="${imagePath}"`);
|
|
||||||
} else {
|
|
||||||
let imagePath = path.join(__dirname, 'template', attachmentTpl, 'image', attachment);
|
|
||||||
body = body.replace(tplImages[i], `src="file:///${imagePath}"`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cb(null, body);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get template attachments
|
|
||||||
* @param {String} template - Template name
|
|
||||||
* @param {String} body - template body
|
|
||||||
* @param {Object} cb - Callback
|
|
||||||
*/
|
|
||||||
getAttachments: function(template, body, isPreview, cb) {
|
|
||||||
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 src = tplAttachments[i].replace('src="cid:', '').replace('"', '').split('/');
|
|
||||||
let attachmentTpl = src[0];
|
|
||||||
let attachment = src[1];
|
|
||||||
|
|
||||||
if (isPreview) {
|
|
||||||
let attachmentPath = `/mailer/static/${attachmentTpl}/${attachment}`;
|
|
||||||
body = body.replace(tplAttachments[i], `src="${attachmentPath}"`);
|
|
||||||
} else {
|
|
||||||
let attachmentPath = path.join(__dirname, 'template', `${attachmentTpl}`, 'image', attachment);
|
|
||||||
let attachmentName = attachmentTpl + '/' + attachment;
|
|
||||||
attachments.push({filename: attachmentName, path: attachmentPath, cid: attachmentName});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPreview)
|
|
||||||
return cb(null, {body: body, attachments: attachments});
|
|
||||||
|
|
||||||
// Template attachment files
|
|
||||||
let attachmentsPath = path.join(__dirname, 'template', `${template}`, 'attachment.json');
|
|
||||||
|
|
||||||
fs.stat(attachmentsPath, (error, stats) => {
|
|
||||||
if (error)
|
|
||||||
return cb(null, {body: body, attachments: attachments});
|
|
||||||
|
|
||||||
let attachObj = require(attachmentsPath);
|
|
||||||
|
|
||||||
for (var i = 0; i < attachObj.length; i++) {
|
|
||||||
let filename = attachObj[i];
|
|
||||||
let attachmentPath = path.join(__dirname, 'template', `${template}`, 'attachment', filename);
|
|
||||||
|
|
||||||
attachments.push({filename: filename, path: attachmentPath, cid: filename});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.checkAttachments(attachments, error => {
|
|
||||||
if (error)
|
|
||||||
return cb(error);
|
|
||||||
cb(null, {body: body, attachments: attachments});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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];
|
|
||||||
fs.stat(attachment.path, error => {
|
|
||||||
if (error)
|
|
||||||
return cb(new Error(`Could not load attachment file ${attachment.path}`));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cb();
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,56 +0,0 @@
|
||||||
var path = require('path');
|
|
||||||
var database = require(path.join(__dirname, '../../database.js'));
|
|
||||||
let strftime = require('strftime');
|
|
||||||
|
|
||||||
module.exports = class ClaimPickup {
|
|
||||||
async getData(params, cb) {
|
|
||||||
let qryData = `
|
|
||||||
SELECT
|
|
||||||
c.id clientId,
|
|
||||||
cl.id claimId,
|
|
||||||
LOWER(ct.code) AS countryCode,
|
|
||||||
c.email AS recipient,
|
|
||||||
c.socialName,
|
|
||||||
c.name AS clientName,
|
|
||||||
c.street,
|
|
||||||
c.postcode,
|
|
||||||
c.city,
|
|
||||||
c.fi,
|
|
||||||
p.name AS province,
|
|
||||||
ct.country
|
|
||||||
FROM claim cl
|
|
||||||
JOIN client c ON c.id = cl.clientFk
|
|
||||||
JOIN country ct ON ct.id = c.countryFk
|
|
||||||
LEFT JOIN province p ON p.id = c.provinceFk
|
|
||||||
WHERE cl.id = ?`;
|
|
||||||
|
|
||||||
let qryLines = `
|
|
||||||
SELECT
|
|
||||||
s.id,
|
|
||||||
s.quantity,
|
|
||||||
s.concept,
|
|
||||||
cb.quantity claimQuantity
|
|
||||||
FROM claimBeginning cb
|
|
||||||
JOIN sale s ON s.id = cb.saleFk
|
|
||||||
WHERE cb.claimFk = ?`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
let [data] = await database.pool.query(qryData, [params.claimId]);
|
|
||||||
|
|
||||||
if (!data)
|
|
||||||
throw new Error('No body data found');
|
|
||||||
|
|
||||||
let [lines] = await database.pool.query(qryLines, [params.claimId]);
|
|
||||||
|
|
||||||
Object.assign(this, data[0]);
|
|
||||||
this.lines = lines;
|
|
||||||
cb();
|
|
||||||
} catch (e) {
|
|
||||||
cb(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get currentDate() {
|
|
||||||
return strftime('%d-%m-%Y', new Date());
|
|
||||||
}
|
|
||||||
};
|
|
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 8.4 KiB |
|
@ -1,48 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
|
||||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" x="0px" y="0px" width="226.229px" height="31.038px" viewBox="0 0 226.229 31.038" enable-background="new 0 0 226.229 31.038" xml:space="preserve" id="svg2" inkscape:version="0.48.1 r9760" sodipodi:docname="logo.svg"><metadata id="metadata61"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata><defs id="defs59"/><sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="1366" inkscape:window-height="710" id="namedview57" showgrid="false" inkscape:zoom="4.0755163" inkscape:cx="138.56745" inkscape:cy="16.509992" inkscape:window-x="0" inkscape:window-y="26" inkscape:window-maximized="1" inkscape:current-layer="svg2"/>
|
|
||||||
<g id="Background">
|
|
||||||
</g>
|
|
||||||
<g id="Guides">
|
|
||||||
</g>
|
|
||||||
<g id="Foreground">
|
|
||||||
<g id="g7">
|
|
||||||
<g id="g9">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.417,30.321L0,0h8.233l4.26,15.582l0.349,1.276 c0.521,1.866,0.918,3.431,1.191,4.693c0.15-0.618,0.335-1.345,0.555-2.182c0.219-0.837,0.528-1.935,0.925-3.293L19.981,0h8.19 L17.671,30.321H10.417z" id="path11"/>
|
|
||||||
</g>
|
|
||||||
<g id="g13">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#A0CE67" d="M139.809,19.787c-0.665,0.357-1.748,0.686-3.25,0.988 c-0.727,0.137-1.283,0.254-1.667,0.35c-0.95,0.247-1.661,0.563-2.134,0.947c-0.472,0.384-0.799,0.899-0.979,1.544 c-0.223,0.796-0.155,1.438,0.204,1.925c0.359,0.488,0.945,0.731,1.757,0.731c1.252,0,2.375-0.36,3.369-1.081 c0.994-0.721,1.653-1.665,1.98-2.831L139.809,19.787z M144.915,30.321h-7.458c0.017-0.356,0.048-0.726,0.094-1.11l0.159-1.192 c-1.318,1.026-2.627,1.786-3.927,2.279c-1.299,0.493-2.643,0.739-4.031,0.739c-2.158,0-3.7-0.593-4.625-1.779 c-0.925-1.187-1.106-2.788-0.542-4.804c0.519-1.851,1.431-3.356,2.737-4.515c1.307-1.159,3.021-1.972,5.142-2.438 c1.169-0.247,2.641-0.515,4.413-0.803c2.646-0.412,4.082-1.016,4.304-1.812l0.151-0.539c0.182-0.65,0.076-1.145-0.317-1.483 c-0.393-0.339-1.071-0.508-2.033-0.508c-1.045,0-1.934,0.214-2.666,0.643c-0.731,0.428-1.289,1.058-1.673,1.887h-6.748 c1.065-2.53,2.64-4.413,4.723-5.65s4.724-1.856,7.923-1.856c1.991,0,3.602,0.241,4.833,0.722s2.095,1.209,2.59,2.185 c0.339,0.701,0.483,1.536,0.432,2.504c-0.052,0.969-0.377,2.525-0.978,4.669l-2.375,8.483c-0.284,1.014-0.416,1.812-0.396,2.395 s0.188,0.962,0.503,1.141L144.915,30.321z" id="path15" style="fill:#8ed300;fill-opacity:1"/>
|
|
||||||
</g>
|
|
||||||
<g id="g17">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#A0CE67" d="M185.7,30.321l6.27-22.393h7.049l-1.097,3.918 c1.213-1.537,2.502-2.659,3.867-3.366c1.365-0.707,2.951-1.074,4.758-1.101l-2.03,7.25c-0.304-0.042-0.608-0.072-0.912-0.093 c-0.303-0.02-0.592-0.03-0.867-0.03c-1.126,0-2.104,0.168-2.932,0.504c-0.829,0.336-1.561,0.854-2.197,1.555 c-0.406,0.467-0.789,1.136-1.149,2.007c-0.361,0.872-0.814,2.282-1.359,4.232l-2.104,7.516H185.7z" id="path19" style="fill:#8ed300;fill-opacity:1"/>
|
|
||||||
</g>
|
|
||||||
<g id="g21">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#A0CE67" d="M217.631,19.787c-0.664,0.357-1.748,0.686-3.25,0.988 c-0.727,0.137-1.282,0.254-1.667,0.35c-0.95,0.247-1.661,0.563-2.134,0.947c-0.472,0.384-0.799,0.899-0.979,1.544 c-0.223,0.796-0.155,1.438,0.205,1.925c0.359,0.488,0.945,0.731,1.757,0.731c1.252,0,2.375-0.36,3.369-1.081 c0.994-0.721,1.654-1.665,1.98-2.831L217.631,19.787z M222.737,30.321h-7.458c0.017-0.356,0.048-0.726,0.094-1.11l0.159-1.192 c-1.318,1.026-2.627,1.786-3.927,2.279c-1.299,0.493-2.643,0.739-4.031,0.739c-2.158,0-3.7-0.593-4.625-1.779 c-0.926-1.187-1.106-2.788-0.542-4.804c0.519-1.851,1.431-3.356,2.737-4.515c1.306-1.159,3.02-1.972,5.142-2.438 c1.169-0.247,2.641-0.515,4.413-0.803c2.647-0.412,4.082-1.016,4.304-1.812l0.151-0.539c0.182-0.65,0.077-1.145-0.317-1.483 c-0.393-0.339-1.071-0.508-2.033-0.508c-1.045,0-1.934,0.214-2.666,0.643c-0.731,0.428-1.289,1.058-1.672,1.887h-6.748 c1.065-2.53,2.64-4.413,4.723-5.65s4.724-1.856,7.923-1.856c1.99,0,3.601,0.241,4.833,0.722s2.095,1.209,2.591,2.185 c0.339,0.701,0.483,1.536,0.431,2.504c-0.051,0.969-0.377,2.525-0.978,4.669l-2.375,8.483c-0.284,1.014-0.416,1.812-0.396,2.395 c0.02,0.583,0.188,0.962,0.503,1.141L222.737,30.321z" id="path23" style="fill:#8ed300;fill-opacity:1"/>
|
|
||||||
</g>
|
|
||||||
<g id="g25">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#A0CE67" d="M188.386,7.928l-6.269,22.393h-7.174l0.864-3.085 c-1.227,1.246-2.476,2.163-3.746,2.751s-2.625,0.882-4.067,0.882c-2.471,0-4.154-0.634-5.048-1.901 c-0.895-1.268-0.993-3.149-0.294-5.644l4.31-15.396h7.338l-3.508,12.53c-0.516,1.842-0.641,3.109-0.375,3.803 s0.967,1.041,2.105,1.041c1.275,0,2.323-0.422,3.142-1.267c0.819-0.845,1.497-2.223,2.031-4.133l3.353-11.974H188.386z" id="path27" style="fill:#8ed300;fill-opacity:1"/>
|
|
||||||
</g>
|
|
||||||
<g id="g29">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#A0CE67" d="M149.937,12.356l1.239-4.428h2.995l1.771-6.326h7.338 l-1.771,6.326h3.753l-1.24,4.428h-3.753l-2.716,9.702c-0.416,1.483-0.498,2.465-0.247,2.946c0.25,0.48,0.905,0.721,1.964,0.721 l0.549-0.011l0.39-0.031l-1.31,4.678c-0.811,0.148-1.596,0.263-2.354,0.344c-0.758,0.081-1.48,0.122-2.167,0.122 c-2.543,0-4.108-0.621-4.695-1.863c-0.587-1.242-0.313-3.887,0.82-7.936l2.428-8.672H149.937z" id="path31" style="fill:#8ed300;fill-opacity:1"/>
|
|
||||||
</g>
|
|
||||||
<g id="g33">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M73.875,18.896c-0.561,2.004-0.616,3.537-0.167,4.601 s1.375,1.595,2.774,1.595c1.399,0,2.605-0.524,3.62-1.574s1.806-2.59,2.375-4.622c0.526-1.879,0.556-3.334,0.09-4.363 c-0.466-1.029-1.393-1.543-2.778-1.543c-1.304,0-2.487,0.528-3.551,1.585S74.386,17.071,73.875,18.896z M96.513,0l-8.489,30.321 h-7.337l0.824-2.944c-1.166,1.22-2.369,2.121-3.61,2.703s-2.583,0.874-4.025,0.874c-2.802,0-4.772-1.081-5.912-3.243 c-1.139-2.162-1.218-4.993-0.238-8.493c0.988-3.528,2.668-6.404,5.042-8.627c2.374-2.224,4.927-3.336,7.661-3.336 c1.47,0,2.695,0.296,3.676,0.887c0.981,0.591,1.681,1.465,2.099,2.62L89.217,0H96.513z" id="path35"/>
|
|
||||||
<g id="g37">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M73.875,18.896c-0.561,2.004-0.616,3.537-0.167,4.601s1.375,1.595,2.774,1.595 c1.399,0,2.605-0.524,3.62-1.574s1.806-2.59,2.375-4.622c0.526-1.879,0.556-3.334,0.09-4.363 c-0.466-1.029-1.393-1.543-2.778-1.543c-1.304,0-2.487,0.528-3.551,1.585S74.386,17.071,73.875,18.896z M96.513,0l-8.489,30.321 h-7.337l0.824-2.944c-1.166,1.22-2.369,2.121-3.61,2.703s-2.583,0.874-4.025,0.874c-2.802,0-4.772-1.081-5.912-3.243 c-1.139-2.162-1.218-4.993-0.238-8.493c0.988-3.528,2.668-6.404,5.042-8.627c2.374-2.224,4.927-3.336,7.661-3.336 c1.47,0,2.695,0.296,3.676,0.887c0.981,0.591,1.681,1.465,2.099,2.62L89.217,0H96.513z" id="path39"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g id="g41">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.488,30.321l6.269-22.393h7.049l-1.098,3.918 c1.213-1.537,2.502-2.659,3.868-3.366s6.015-1.074,7.822-1.101l-2.03,7.25c-0.304-0.042-0.608-0.072-0.911-0.093 c-0.304-0.02-0.592-0.03-0.867-0.03c-1.126,0-5.167,0.168-5.997,0.504c-0.829,0.336-1.561,0.854-2.196,1.555 c-0.406,0.467-0.789,1.136-1.149,2.007c-0.361,0.872-0.814,2.282-1.36,4.232l-2.104,7.516H46.488z" id="path43"/>
|
|
||||||
</g>
|
|
||||||
<g id="g45">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M32.673,16.742l8.351-0.021 c0.375-1.436,0.308-2.558-0.201-3.365s-1.402-1.211-2.68-1.211c-1.209,0-2.285,0.397-3.229,1.19S33.224,15.265,32.673,16.742z M38.817,23.278h7.043c-1.347,2.456-3.172,4.356-5.477,5.7c-2.305,1.345-4.885,2.017-7.74,2.017 c-3.473,0-5.923-1.054-7.351-3.161c-1.427-2.107-1.632-4.98-0.613-8.618c1.038-3.707,2.875-6.641,5.512-8.803 c2.637-2.163,5.678-3.244,9.123-3.244c3.555,0,6.04,1.099,7.456,3.298c1.417,2.198,1.582,5.234,0.498,9.109l-0.239,0.814 l-0.167,0.484H31.721c-0.441,1.575-0.438,2.777,0.01,3.606c0.448,0.829,1.332,1.244,2.65,1.244c0.975,0,1.836-0.206,2.583-0.617 S38.33,24.086,38.817,23.278z" id="path47"/>
|
|
||||||
<g id="g49">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.673,16.742l8.351-0.021c0.375-1.436,0.308-2.558-0.201-3.365 s-1.402-1.211-2.68-1.211c-1.209,0-2.285,0.397-3.229,1.19S33.224,15.265,32.673,16.742z M38.817,23.278h7.043 c-1.347,2.456-3.172,4.356-5.477,5.7c-2.305,1.345-4.885,2.017-7.74,2.017c-3.473,0-5.923-1.054-7.351-3.161 c-1.427-2.107-1.632-4.98-0.613-8.618c1.038-3.707,2.875-6.641,5.512-8.803c2.637-2.163,5.678-3.244,9.123-3.244 c3.555,0,6.04,1.099,7.456,3.298c1.417,2.198,1.582,5.234,0.498,9.109l-0.239,0.814l-0.167,0.484H31.721 c-0.441,1.575-0.438,2.777,0.01,3.606c0.448,0.829,1.332,1.244,2.65,1.244c0.975,0,1.836-0.206,2.583-0.617 S38.33,24.086,38.817,23.278z" id="path51"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g id="g53">
|
|
||||||
<path fill="#A0CE67" d="M112.881,30.643l-6.404-18.639l-6.455,18.639h-7.254l9.565-30.321h8.19l4.434,15.582l0.35,1.276 c0.521,1.866,0.917,3.431,1.191,4.693l0.555-2.182c0.219-0.837,0.528-1.935,0.925-3.293l4.468-16.076h8.19l-10.501,30.321 H112.881z" id="path55" style="fill:#8ed300;fill-opacity:1"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 9.5 KiB |