check default true #1132

Merged
pau merged 5 commits from 4484-ticket-withOutNegatives-default-true into dev 2022-11-15 07:56:52 +00:00
136 changed files with 43869 additions and 4158 deletions
Showing only changes of commit dc64d619f6 - Show all commits

3
.gitignore vendored
View File

@ -2,10 +2,11 @@ coverage
node_modules
dist
storage
.idea
npm-debug.log
.eslintcache
datasources.*.json
print.*.json
db.json
junit.xml
.DS_Store
.DS_Store

12
.vscode/settings.json vendored
View File

@ -2,7 +2,13 @@
{
// Carácter predeterminado de final de línea.
"files.eol": "\n",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
"editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": true,
"editor.formatOnSave": true,
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
"eslint.validate": [
"javascript",
"json"
]
}

View File

@ -1,32 +1,43 @@
FROM debian:stretch-slim
FROM debian:bullseye-slim
ENV TZ Europe/Madrid
ARG DEBIAN_FRONTEND=noninteractive
# NodeJs
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
curl \
ca-certificates \
gnupg2 \
libfontconfig lftp \
&& apt-get -y install xvfb gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 \
libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 \
libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 \
libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 \
libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget \
&& curl -sL https://deb.nodesource.com/setup_14.x | bash - \
&& curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& npm install -g npm@8.19.2
# Puppeteer
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
nodejs \
&& apt-get purge -y --auto-remove \
gnupg2 \
libfontconfig lftp xvfb gconf-service libasound2 libatk1.0-0 libc6 \
libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 \
libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 \
libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 \
libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 \
libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \
fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget \
&& rm -rf /var/lib/apt/lists/* \
&& npm -g install pm2
# Salix
WORKDIR /salix
COPY print/package.json print/package-lock.json print/
RUN npm --prefix ./print install --omit=dev ./print
COPY package.json package-lock.json ./
COPY loopback/package.json loopback/
COPY print/package.json print/
RUN npm install --only=prod
RUN npm --prefix ./print install --only=prod ./print
RUN npm install --omit=dev
COPY loopback loopback
COPY back back

15
Jenkinsfile vendored
View File

@ -1,5 +1,4 @@
#!/usr/bin/env groovy
pipeline {
agent any
options {
@ -62,13 +61,13 @@ pipeline {
}
}
}
// stage('Backend') {
// steps {
// nodejs('node-v14') {
// sh 'npm run test:back:ci'
// }
// }
// }
stage('Backend') {
steps {
nodejs('node-v14') {
sh 'npm run test:back:ci'
}
}
}
}
}
stage('Build') {

View File

@ -47,20 +47,22 @@ module.exports = Self => {
for (let dms of dmsToDelete) {
const pathHash = DmsContainer.getHash(dms.id);
const dmsContainer = await DmsContainer.container(pathHash);
const dstFile = path.join(dmsContainer.client.root, pathHash, dms.file);
try {
const dstFile = path.join(dmsContainer.client.root, pathHash, dms.file);
await fs.unlink(dstFile);
} catch (err) {
continue;
if (err.code != 'ENOENT')
throw err;
}
await dms.destroy(myOptions);
const dstFolder = path.join(dmsContainer.client.root, pathHash);
try {
await fs.rmdir(dstFolder);
} catch (err) {
continue;
}
await dms.destroy(myOptions);
}
};
};

View File

@ -1,4 +1,4 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('InvoiceIn', 'invoiceInPdf', 'READ', 'ALLOW', 'ROLE', 'administrative'),
('InvoiceIn', 'invoiceInEmail', 'WRITE', 'ALLOW', 'ROLE', 'administrative'),
('InvoiceIn', 'invoiceInEmail', 'WRITE', 'ALLOW', 'ROLE', 'administrative');

View File

@ -0,0 +1 @@
ALTER TABLE `vn`.`workerTimeControlMail` CHANGE emailResponse reason text CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL NULL;

View File

View File

@ -918,7 +918,7 @@ INSERT INTO `vn`.`expeditionStateType`(`id`, `description`, `code`)
(3, 'Perdida', 'LOST');
INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `freightItemFk`, `created`, `itemFk`, `counter`, `workerFk`, `externalId`, `packagingFk`, `stateTypeFk`)
INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `freightItemFk`, `created`, `itemFk`, `counter`, `workerFk`, `externalId`, `packagingFk`, `stateTypeFk`, `hostFk`)
VALUES
(1, 1, 1, 71, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 15, 1, 18, 'UR9000006041', 94, 1, 'pc1'),
(2, 1, 1, 71, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 16, 2, 18, 'UR9000006041', 94, 1, NULL),
@ -2267,12 +2267,16 @@ INSERT INTO `vn`.`zoneEvent`(`zoneFk`, `type`, `started`, `ended`)
VALUES
(9, 'range', DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR), DATE_ADD(util.VN_CURDATE(), INTERVAL +1 YEAR));
INSERT INTO `vn`.`workerTimeControl`(`userFk`, `timed`, `manual`, `direction`)
INSERT INTO `vn`.`workerTimeControl`(`userFk`, `timed`, `manual`, `direction`, `isSendMail`)
VALUES
(1106, CONCAT(util.VN_CURDATE(), ' 07:00'), TRUE, 'in'),
(1106, CONCAT(util.VN_CURDATE(), ' 10:00'), TRUE, 'middle'),
(1106, CONCAT(util.VN_CURDATE(), ' 10:20'), TRUE, 'middle'),
(1106, CONCAT(util.VN_CURDATE(), ' 14:50'), TRUE, 'out');
(1106, CONCAT(util.VN_CURDATE(), ' 07:00'), TRUE, 'in', 0),
(1106, CONCAT(util.VN_CURDATE(), ' 10:00'), TRUE, 'middle', 0),
(1106, CONCAT(util.VN_CURDATE(), ' 10:20'), TRUE, 'middle', 0),
(1106, CONCAT(util.VN_CURDATE(), ' 14:50'), TRUE, 'out', 0),
(1107, CONCAT(util.VN_CURDATE(), ' 07:00'), TRUE, 'in', 1),
(1107, CONCAT(util.VN_CURDATE(), ' 10:00'), TRUE, 'middle', 1),
(1107, CONCAT(util.VN_CURDATE(), ' 10:20'), TRUE, 'middle', 1),
(1107, CONCAT(util.VN_CURDATE(), ' 14:50'), TRUE, 'out', 1);
INSERT INTO `vn`.`dmsType`(`id`, `name`, `path`, `readRoleFk`, `writeRoleFk`, `code`)
VALUES
@ -2714,4 +2718,4 @@ UPDATE `account`.`user`
INSERT INTO `vn`.`osTicketConfig` (`id`, `host`, `user`, `password`, `oldStatus`, `newStatusId`, `day`, `comment`, `hostDb`, `userDb`, `passwordDb`, `portDb`, `responseType`, `fromEmailId`, `replyTo`)
VALUES
(0, 'http://localhost:56596/scp', 'ostadmin', 'Admin1', 'open', 3, 60, 'Este CAU se ha cerrado automáticamente. Si el problema persiste responda a este mensaje.', 'localhost', 'osticket', 'osticket', 40003, 'reply', 1, 'all');
(0, 'http://localhost:56596/scp', 'ostadmin', 'Admin1', 'open', 3, 60, 'Este CAU se ha cerrado automáticamente. Si el problema persiste responda a este mensaje.', 'localhost', 'osticket', 'osticket', 40003, 'reply', 1, 'all');

View File

@ -24,10 +24,11 @@
}
},
"node_modules/@uirouter/angularjs": {
"version": "1.0.29",
"license": "MIT",
"version": "1.0.30",
"resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.30.tgz",
"integrity": "sha512-qkc3RFZc91S5K0gc/QVAXc9LGDPXjR04vDgG/11j8+yyZEuQojXxKxdLhKIepiPzqLmGRVqzBmBc27gtqaEeZg==",
"dependencies": {
"@uirouter/core": "6.0.7"
"@uirouter/core": "6.0.8"
},
"engines": {
"node": ">=4.0.0"
@ -37,15 +38,18 @@
}
},
"node_modules/@uirouter/core": {
"version": "6.0.7",
"license": "MIT",
"version": "6.0.8",
"resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.8.tgz",
"integrity": "sha512-Gc/BAW47i4L54p8dqYCJJZuv2s3tqlXQ0fvl6Zp2xrblELPVfxmjnc0eurx3XwfQdaqm3T6uls6tQKkof/4QMw==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/angular": {
"version": "1.8.2",
"license": "MIT"
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/angular/-/angular-1.8.3.tgz",
"integrity": "sha512-5qjkWIQQVsHj4Sb5TcEs4WZWpFeVFHXwxEBHUhrny41D8UrBAd6T/6nPPAsLngJCReIOqi95W3mxdveveutpZw==",
"deprecated": "For the actively supported Angular, see https://www.npmjs.com/package/@angular/core. AngularJS support has officially ended. For extended AngularJS support options, see https://goo.gle/angularjs-path-forward."
},
"node_modules/angular-animate": {
"version": "1.8.2",
@ -62,8 +66,9 @@
}
},
"node_modules/angular-translate": {
"version": "2.18.4",
"license": "MIT",
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.19.0.tgz",
"integrity": "sha512-Z/Fip5uUT2N85dPQ0sMEe1JdF5AehcDe4tg/9mWXNDVU531emHCg53ZND9Oe0dyNiGX5rWcJKmsL1Fujus1vGQ==",
"dependencies": {
"angular": "^1.8.0"
},
@ -72,10 +77,11 @@
}
},
"node_modules/angular-translate-loader-partial": {
"version": "2.18.4",
"license": "MIT",
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.19.0.tgz",
"integrity": "sha512-NnMw13LMV4bPQmJK7/pZOZAnPxe0M5OtUHchADs5Gye7V7feonuEnrZ8e1CKhBlv9a7IQyWoqcBa4Lnhg8gk5w==",
"dependencies": {
"angular-translate": "~2.18.4"
"angular-translate": "~2.19.0"
}
},
"node_modules/argparse": {
@ -119,8 +125,9 @@
}
},
"node_modules/moment": {
"version": "2.29.1",
"license": "MIT",
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"engines": {
"node": "*"
}
@ -164,16 +171,22 @@
},
"dependencies": {
"@uirouter/angularjs": {
"version": "1.0.29",
"version": "1.0.30",
"resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.30.tgz",
"integrity": "sha512-qkc3RFZc91S5K0gc/QVAXc9LGDPXjR04vDgG/11j8+yyZEuQojXxKxdLhKIepiPzqLmGRVqzBmBc27gtqaEeZg==",
"requires": {
"@uirouter/core": "6.0.7"
"@uirouter/core": "6.0.8"
}
},
"@uirouter/core": {
"version": "6.0.7"
"version": "6.0.8",
"resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.8.tgz",
"integrity": "sha512-Gc/BAW47i4L54p8dqYCJJZuv2s3tqlXQ0fvl6Zp2xrblELPVfxmjnc0eurx3XwfQdaqm3T6uls6tQKkof/4QMw=="
},
"angular": {
"version": "1.8.2"
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/angular/-/angular-1.8.3.tgz",
"integrity": "sha512-5qjkWIQQVsHj4Sb5TcEs4WZWpFeVFHXwxEBHUhrny41D8UrBAd6T/6nPPAsLngJCReIOqi95W3mxdveveutpZw=="
},
"angular-animate": {
"version": "1.8.2"
@ -185,15 +198,19 @@
}
},
"angular-translate": {
"version": "2.18.4",
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.19.0.tgz",
"integrity": "sha512-Z/Fip5uUT2N85dPQ0sMEe1JdF5AehcDe4tg/9mWXNDVU531emHCg53ZND9Oe0dyNiGX5rWcJKmsL1Fujus1vGQ==",
"requires": {
"angular": "^1.8.0"
}
},
"angular-translate-loader-partial": {
"version": "2.18.4",
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.19.0.tgz",
"integrity": "sha512-NnMw13LMV4bPQmJK7/pZOZAnPxe0M5OtUHchADs5Gye7V7feonuEnrZ8e1CKhBlv9a7IQyWoqcBa4Lnhg8gk5w==",
"requires": {
"angular-translate": "~2.18.4"
"angular-translate": "~2.19.0"
}
},
"argparse": {
@ -222,7 +239,9 @@
}
},
"moment": {
"version": "2.29.1"
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
},
"oclazyload": {
"version": "0.6.3"

View File

@ -153,6 +153,7 @@
"Email already exists": "Email already exists",
"User already exists": "User already exists",
"Absence change notification on the labour calendar": "Notificacion de cambio de ausencia en el calendario laboral",
"Record of hours week": "Registro de horas semana {{week}} año {{year}} ",
"Created absence": "El empleado <strong>{{author}}</strong> ha añadido una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> para el día {{dated}}.",
"Deleted absence": "El empleado <strong>{{author}}</strong> ha eliminado una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> del día {{dated}}.",
"I have deleted the ticket id": "He eliminado el ticket id [{{id}}]({{{url}}})",
@ -237,8 +238,10 @@
"Modifiable password only via recovery or by an administrator": "Contraseña modificable solo a través de la recuperación o por un administrador",
"Not enough privileges to edit a client": "No tienes suficientes privilegios para editar un cliente",
"This route does not exists": "Esta ruta no existe",
"Claim pickup order sent": "Reclamación Orden de recogida enviada [({{claimId}})]({{{claimUrl}}}) al cliente *{{clientName}}*",
"You don't have grant privilege": "No tienes privilegios para dar privilegios",
"You don't own the role and you can't assign it to another user": "No eres el propietario del rol y no puedes asignarlo a otro usuario",
"Claim pickup order sent": "Reclamación Orden de recogida enviada [({{claimId}})]({{{claimUrl}}}) al cliente *{{clientName}}*",
"You don't have grant privilege": "No tienes privilegios para dar privilegios",
"You don't own the role and you can't assign it to another user": "No eres el propietario del rol y no puedes asignarlo a otro usuario",
"Already has this status": "Ya tiene este estado",
"There aren't records for this week": "No existen registros para esta semana",
"Empty data source": "Origen de datos vacio"
}

View File

@ -1,181 +0,0 @@
const Imap = require('imap');
module.exports = Self => {
Self.remoteMethod('checkInbox', {
description: 'Check an email inbox and process it',
accessType: 'READ',
returns:
{
arg: 'body',
type: 'file',
root: true
},
http: {
path: `/checkInbox`,
verb: 'POST'
}
});
Self.checkInbox = async() => {
let imapConfig = await Self.app.models.WorkerTimeControlParams.findOne();
let imap = new Imap({
user: imapConfig.mailUser,
password: imapConfig.mailPass,
host: imapConfig.mailHost,
port: 993,
tls: true
});
let isEmailOk;
let uid;
let emailBody;
function openInbox(cb) {
imap.openBox('INBOX', true, cb);
}
imap.once('ready', function() {
openInbox(function(err, box) {
if (err) throw err;
const totalMessages = box.messages.total;
if (totalMessages == 0)
imap.end();
let f = imap.seq.fetch('1:*', {
bodies: ['HEADER.FIELDS (FROM SUBJECT)', '1'],
struct: true
});
f.on('message', function(msg, seqno) {
isEmailOk = false;
msg.on('body', function(stream, info) {
let buffer = '';
let bufferCopy = '';
stream.on('data', function(chunk) {
buffer = chunk.toString('utf8');
if (info.which === '1' && bufferCopy.length == 0)
bufferCopy = buffer.replace(/\s/g, ' ');
});
stream.on('end', function() {
if (bufferCopy.length > 0) {
emailBody = bufferCopy.toUpperCase().trim();
const bodyPositionOK = emailBody.match(/\bOK\b/i);
if (bodyPositionOK != null && (bodyPositionOK.index == 0 || bodyPositionOK.index == 122))
isEmailOk = true;
else
isEmailOk = false;
}
});
msg.once('attributes', function(attrs) {
uid = attrs.uid;
});
msg.once('end', function() {
if (info.which === 'HEADER.FIELDS (FROM SUBJECT)') {
if (isEmailOk) {
imap.move(uid, 'exito', function(err) {
});
emailConfirm(buffer);
} else {
imap.move(uid, 'error', function(err) {
});
emailReply(buffer, emailBody);
}
}
});
});
});
f.once('end', function() {
imap.end();
});
});
});
imap.connect();
return 'Leer emails de gestion horaria';
};
async function emailConfirm(buffer) {
const now = new Date();
const from = JSON.stringify(Imap.parseHeader(buffer).from);
const subject = JSON.stringify(Imap.parseHeader(buffer).subject);
const timeControlDate = await getEmailDate(subject);
const week = timeControlDate[0];
const year = timeControlDate[1];
const user = await getUser(from);
let workerMail;
if (user.id != null) {
workerMail = await Self.app.models.WorkerTimeControlMail.findOne({
where: {
week: week,
year: year,
workerFk: user.id
}
});
}
if (workerMail != null) {
await workerMail.updateAttributes({
updated: now,
state: 'CONFIRMED'
});
}
}
async function emailReply(buffer, emailBody) {
const now = new Date();
const from = JSON.stringify(Imap.parseHeader(buffer).from);
const subject = JSON.stringify(Imap.parseHeader(buffer).subject);
const timeControlDate = await getEmailDate(subject);
const week = timeControlDate[0];
const year = timeControlDate[1];
const user = await getUser(from);
let workerMail;
if (user.id != null) {
workerMail = await Self.app.models.WorkerTimeControlMail.findOne({
where: {
week: week,
year: year,
workerFk: user.id
}
});
if (workerMail != null) {
await workerMail.updateAttributes({
updated: now,
state: 'REVISE',
emailResponse: emailBody
});
} else
await sendMail(user, subject, emailBody);
}
}
async function getUser(workerEmail) {
const userEmail = workerEmail.match(/(?<=<)(.*?)(?=>)/);
let [user] = await Self.rawSql(`SELECT u.id,u.name FROM account.user u
LEFT JOIN account.mailForward m on m.account = u.id
WHERE forwardTo =? OR
CONCAT(u.name,'@verdnatura.es') = ?`,
[userEmail[0], userEmail[0]]);
return user;
}
async function getEmailDate(subject) {
const date = subject.match(/\d+/g);
return date;
}
async function sendMail(user, subject, emailBody) {
const sendTo = 'rrhh@verdnatura.es';
const emailSubject = subject + ' ' + user.name;
await Self.app.models.Mail.create({
receiver: sendTo,
subject: emailSubject,
body: emailBody
});
}
};

View File

@ -0,0 +1,377 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = Self => {
Self.remoteMethodCtx('sendMail', {
description: `Send an email with the hours booked to the employees who telecommuting.
It also inserts booked hours in cases where the employee is telecommuting`,
accessType: 'WRITE',
accepts: [{
arg: 'workerId',
type: 'number',
description: 'The worker id'
},
{
arg: 'week',
type: 'number'
},
{
arg: 'year',
type: 'number'
}],
returns: [{
type: 'Object',
root: true
}],
http: {
path: `/sendMail`,
verb: 'POST'
}
});
Self.sendMail = async(ctx, options) => {
const models = Self.app.models;
const conn = Self.dataSource.connector;
const args = ctx.args;
const $t = ctx.req.__; // $translate
let tx;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
const stmts = [];
let stmt;
try {
if (!args.week || !args.year) {
const from = new Date();
const to = new Date();
const time = await models.Time.findOne({
where: {
dated: {between: [from.setDate(from.getDate() - 10), to.setDate(to.getDate() - 4)]}
},
order: 'week ASC'
}, myOptions);
args.week = time.week;
args.year = time.year;
}
const started = getStartDateOfWeekNumber(args.week, args.year);
started.setHours(0, 0, 0, 0);
const ended = new Date(started);
ended.setDate(started.getDate() + 6);
ended.setHours(23, 59, 59, 999);
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate');
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeBusinessCalculate');
if (args.workerId) {
await models.WorkerTimeControl.destroyAll({
userFk: args.workerId,
timed: {between: [started, ended]},
isSendMail: true
}, myOptions);
const where = {
workerFk: args.workerId,
year: args.year,
week: args.week
};
await models.WorkerTimeControlMail.updateAll(where, {
updated: new Date(), state: 'SENDED'
}, myOptions);
stmt = new ParameterizedSQL(
`CALL vn.timeControl_calculateByUser(?, ?, ?)
`, [args.workerId, started, ended]);
stmts.push(stmt);
stmt = new ParameterizedSQL(
`CALL vn.timeBusiness_calculateByUser(?, ?, ?)
`, [args.workerId, started, ended]);
stmts.push(stmt);
} else {
await models.WorkerTimeControl.destroyAll({
timed: {between: [started, ended]},
isSendMail: true
}, myOptions);
const where = {
year: args.year,
week: args.week
};
await models.WorkerTimeControlMail.updateAll(where, {
updated: new Date(), state: 'SENDED'
}, myOptions);
stmt = new ParameterizedSQL(`CALL vn.timeControl_calculateAll(?, ?)`, [started, ended]);
stmts.push(stmt);
stmt = new ParameterizedSQL(`CALL vn.timeBusiness_calculateAll(?, ?)`, [started, ended]);
stmts.push(stmt);
}
stmt = new ParameterizedSQL(`
SELECT CONCAT(u.name, '@verdnatura.es') receiver,
u.id workerFk,
tb.dated,
tb.timeWorkDecimal,
tb.timeWorkSexagesimal timeWorkSexagesimal,
tb.timeTable,
tc.timeWorkDecimal timeWorkedDecimal,
tc.timeWorkSexagesimal timeWorkedSexagesimal,
tb.type,
tb.businessFk,
tb.permissionRate,
d.isTeleworking
FROM tmp.timeBusinessCalculate tb
JOIN user u ON u.id = tb.userFk
JOIN department d ON d.id = tb.departmentFk
JOIN business b ON b.id = tb.businessFk
LEFT JOIN tmp.timeControlCalculate tc ON tc.userFk = tb.userFk AND tc.dated = tb.dated
LEFT JOIN worker w ON w.id = u.id
JOIN (SELECT tb.userFk,
SUM(IF(tb.type IS NULL,
IF(tc.timeWorkDecimal > 0, FALSE, IF(tb.timeWorkDecimal > 0, TRUE, FALSE)),
TRUE))isTeleworkingWeek
FROM tmp.timeBusinessCalculate tb
LEFT JOIN tmp.timeControlCalculate tc ON tc.userFk = tb.userFk
AND tc.dated = tb.dated
GROUP BY tb.userFk
HAVING isTeleworkingWeek > 0
)sub ON sub.userFk = u.id
WHERE d.hasToRefill
AND IFNULL(?, u.id) = u.id
AND b.companyCodeFk = 'VNL'
AND w.businessFk
ORDER BY u.id, tb.dated
`, [args.workerId]);
const index = stmts.push(stmt) - 1;
const sql = ParameterizedSQL.join(stmts, ';');
const days = await conn.executeStmt(sql, myOptions);
let previousWorkerFk = days[index][0].workerFk;
let previousReceiver = days[index][0].receiver;
const workerTimeControlConfig = await models.WorkerTimeControlConfig.findOne(null, myOptions);
for (let day of days[index]) {
workerFk = day.workerFk;
if (day.timeWorkDecimal > 0 && day.timeWorkedDecimal == null
&& (day.permissionRate ? day.permissionRate : true)) {
if (day.timeTable == null) {
const timed = new Date(day.dated);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
timed: timed.setHours(8),
manual: true,
direction: 'in',
isSendMail: true
}, myOptions);
if (day.timeWorkDecimal >= workerTimeControlConfig.timeToBreakTime / 3600) {
await models.WorkerTimeControl.create({
userFk: day.workerFk,
timed: timed.setHours(9),
manual: true,
direction: 'middle',
isSendMail: true
}, myOptions);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
timed: timed.setHours(9, 20),
manual: true,
direction: 'middle',
isSendMail: true
}, myOptions);
}
const [hoursWork, minutesWork, secondsWork] = getTime(day.timeWorkSexagesimal);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
timed: timed.setHours(8 + hoursWork, minutesWork, secondsWork),
manual: true,
direction: 'out',
isSendMail: true
}, myOptions);
} else {
const weekDay = day.dated.getDay();
const journeys = await models.Journey.find({
where: {
business_id: day.businessFk,
day_id: weekDay
}
}, myOptions);
let timeTableDecimalInSeconds = 0;
for (let journey of journeys) {
const start = new Date();
const [startHours, startMinutes, startSeconds] = getTime(journey.start);
start.setHours(startHours, startMinutes, startSeconds, 0);
const end = new Date();
const [endHours, endMinutes, endSeconds] = getTime(journey.end);
end.setHours(endHours, endMinutes, endSeconds, 0);
const result = (end - start) / 1000;
timeTableDecimalInSeconds += result;
}
for (let journey of journeys) {
const timeTableDecimal = timeTableDecimalInSeconds / 3600;
if (day.timeWorkDecimal == timeTableDecimal) {
const timed = new Date(day.dated);
const [startHours, startMinutes, startSeconds] = getTime(journey.start);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
timed: timed.setHours(startHours, startMinutes, startSeconds),
manual: true,
isSendMail: true
}, myOptions);
const [endHours, endMinutes, endSeconds] = getTime(journey.end);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
timed: timed.setHours(endHours, endMinutes, endSeconds),
manual: true,
isSendMail: true
}, myOptions);
} else {
const minStart = journeys.reduce(function(prev, curr) {
return curr.start < prev.start ? curr : prev;
});
if (journey == minStart) {
const timed = new Date(day.dated);
const [startHours, startMinutes, startSeconds] = getTime(journey.start);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
timed: timed.setHours(startHours, startMinutes, startSeconds),
manual: true,
isSendMail: true
}, myOptions);
const [hoursWork, minutesWork, secondsWork] = getTime(day.timeWorkSexagesimal);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
timed: timed.setHours(
startHours + hoursWork,
startMinutes + minutesWork,
startSeconds + secondsWork
),
manual: true,
isSendMail: true
}, myOptions);
}
}
if (day.timeWorkDecimal >= workerTimeControlConfig.timeToBreakTime / 3600) {
const minStart = journeys.reduce(function(prev, curr) {
return curr.start < prev.start ? curr : prev;
});
if (journey == minStart) {
const timed = new Date(day.dated);
const [startHours, startMinutes, startSeconds] = getTime(journey.start);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
timed: timed.setHours(startHours + 1, startMinutes, startSeconds),
manual: true,
isSendMail: true
}, myOptions);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
timed: timed.setHours(startHours + 1, startMinutes + 20, startSeconds),
manual: true,
isSendMail: true
}, myOptions);
}
}
}
const timed = new Date(day.dated);
const firstWorkerTimeControl = await models.WorkerTimeControl.findOne({
where: {
userFk: day.workerFk,
timed: {between: [timed.setHours(0, 0, 0, 0), timed.setHours(23, 59, 59, 999)]}
},
order: 'timed ASC'
}, myOptions);
if (firstWorkerTimeControl)
firstWorkerTimeControl.updateAttribute('direction', 'in', myOptions);
const lastWorkerTimeControl = await models.WorkerTimeControl.findOne({
where: {
userFk: day.workerFk,
timed: {between: [timed.setHours(0, 0, 0, 0), timed.setHours(23, 59, 59, 999)]}
},
order: 'timed DESC'
}, myOptions);
if (lastWorkerTimeControl)
lastWorkerTimeControl.updateAttribute('direction', 'out', myOptions);
}
}
const lastDay = days[index][days[index].length - 1];
if (day.workerFk != previousWorkerFk || day == lastDay) {
const salix = await models.Url.findOne({
where: {
appName: 'salix',
environment: process.env.NODE_ENV || 'dev'
}
}, myOptions);
const timestamp = started.getTime() / 1000;
await models.Mail.create({
receiver: previousReceiver,
subject: $t('Record of hours week', {
week: args.week,
year: args.year
}),
body: `${salix.url}worker/${previousWorkerFk}/time-control?timestamp=${timestamp}`
}, myOptions);
query = `INSERT IGNORE INTO workerTimeControlMail (workerFk, year, week)
VALUES (?, ?, ?);`;
await Self.rawSql(query, [previousWorkerFk, args.year, args.week], myOptions);
previousWorkerFk = day.workerFk;
previousReceiver = day.receiver;
}
}
if (tx) await tx.commit();
return true;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
function getStartDateOfWeekNumber(week, year) {
const simple = new Date(year, 0, 1 + (week - 1) * 7);
const dow = simple.getDay();
const weekStart = simple;
if (dow <= 4)
weekStart.setDate(simple.getDate() - simple.getDay() + 1);
else
weekStart.setDate(simple.getDate() + 8 - simple.getDay());
return weekStart;
}
function getTime(timeString) {
const [hours, minutes, seconds] = timeString.split(':');
return [parseInt(hours), parseInt(minutes), parseInt(seconds)];
}
};

View File

@ -0,0 +1,132 @@
const models = require('vn-loopback/server/server').models;
describe('workerTimeControl sendMail()', () => {
const workerId = 18;
const ctx = {
req: {
__: value => {
return value;
}
},
args: {}
};
beforeAll(function() {
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
});
it('should fill time control of a worker without records in Journey and with rest', async() => {
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
await models.WorkerTimeControl.sendMail(ctx, options);
const workerTimeControl = await models.WorkerTimeControl.find({
where: {userFk: workerId}
}, options);
expect(workerTimeControl[0].timed.getHours()).toEqual(8);
expect(workerTimeControl[1].timed.getHours()).toEqual(9);
expect(`${workerTimeControl[2].timed.getHours()}:${workerTimeControl[2].timed.getMinutes()}`).toEqual('9:20');
expect(workerTimeControl[3].timed.getHours()).toEqual(16);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should fill time control of a worker without records in Journey and without rest', async() => {
const workdayOf20Hours = 3;
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
query = `UPDATE business b
SET b.calendarTypeFk = ?
WHERE b.workerFk = ?; `;
await models.WorkerTimeControl.rawSql(query, [workdayOf20Hours, workerId], options);
await models.WorkerTimeControl.sendMail(ctx, options);
const workerTimeControl = await models.WorkerTimeControl.find({
where: {userFk: workerId}
}, options);
expect(workerTimeControl[0].timed.getHours()).toEqual(8);
expect(workerTimeControl[1].timed.getHours()).toEqual(12);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should fill time control of a worker with records in Journey and with rest', async() => {
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
query = `INSERT INTO postgresql.journey(journey_id, day_id, start, end, business_id)
VALUES
(1, 1, '09:00:00', '13:00:00', ?),
(2, 1, '14:00:00', '19:00:00', ?);`;
await models.WorkerTimeControl.rawSql(query, [workerId, workerId, workerId], options);
await models.WorkerTimeControl.sendMail(ctx, options);
const workerTimeControl = await models.WorkerTimeControl.find({
where: {userFk: workerId}
}, options);
expect(workerTimeControl[0].timed.getHours()).toEqual(9);
expect(workerTimeControl[2].timed.getHours()).toEqual(10);
expect(`${workerTimeControl[3].timed.getHours()}:${workerTimeControl[3].timed.getMinutes()}`).toEqual('10:20');
expect(workerTimeControl[1].timed.getHours()).toEqual(13);
expect(workerTimeControl[4].timed.getHours()).toEqual(14);
expect(workerTimeControl[5].timed.getHours()).toEqual(19);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should fill time control of a worker with records in Journey and without rest', async() => {
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
query = `INSERT INTO postgresql.journey(journey_id, day_id, start, end, business_id)
VALUES
(1, 1, '12:30:00', '14:00:00', ?);`;
await models.WorkerTimeControl.rawSql(query, [workerId, workerId, workerId], options);
await models.WorkerTimeControl.sendMail(ctx, options);
const workerTimeControl = await models.WorkerTimeControl.find({
where: {userFk: workerId}
}, options);
expect(`${workerTimeControl[0].timed.getHours()}:${workerTimeControl[0].timed.getMinutes()}`).toEqual('12:30');
expect(workerTimeControl[1].timed.getHours()).toEqual(14);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
afterAll(function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
});
});

View File

@ -0,0 +1,87 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('updateWorkerTimeControlMail', {
description: 'Updates the state of WorkerTimeControlMail',
accessType: 'WRITE',
accepts: [{
arg: 'workerId',
type: 'number',
required: true
},
{
arg: 'year',
type: 'number',
required: true
},
{
arg: 'week',
type: 'number',
required: true
},
{
arg: 'state',
type: 'string',
required: true
},
{
arg: 'reason',
type: 'string'
}],
returns: {
type: 'boolean',
root: true
},
http: {
path: `/updateWorkerTimeControlMail`,
verb: 'POST'
}
});
Self.updateWorkerTimeControlMail = async(ctx, options) => {
const models = Self.app.models;
const args = ctx.args;
const userId = ctx.req.accessToken.userId;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const workerTimeControlMail = await models.WorkerTimeControlMail.findOne({
where: {
workerFk: args.workerId,
year: args.year,
week: args.week
}
}, myOptions);
if (!workerTimeControlMail) throw new UserError(`There aren't records for this week`);
const oldState = workerTimeControlMail.state;
const oldReason = workerTimeControlMail.reason;
if (oldState == args.state) throw new UserError('Already has this status');
await workerTimeControlMail.updateAttributes({
state: args.state,
reason: args.reason || null
}, myOptions);
const logRecord = {
originFk: args.workerId,
userFk: userId,
action: 'update',
changedModel: 'WorkerTimeControlMail',
oldInstance: {
state: oldState,
reason: oldReason
},
newInstance: {
state: args.state,
reason: args.reason
}
};
return models.WorkerLog.create(logRecord, myOptions);
};
};

View File

@ -20,6 +20,12 @@
"EducationLevel": {
"dataSource": "vn"
},
"Journey": {
"dataSource": "vn"
},
"Time": {
"dataSource": "vn"
},
"WorkCenter": {
"dataSource": "vn"
},
@ -59,6 +65,9 @@
"WorkerLog": {
"dataSource": "vn"
},
"WorkerTimeControlConfig": {
"dataSource": "vn"
},
"WorkerTimeControlParams": {
"dataSource": "vn"
},

View File

@ -0,0 +1,27 @@
{
"name": "Journey",
"base": "VnModel",
"options": {
"mysql": {
"table": "postgresql.journey"
}
},
"properties": {
"journey_id": {
"id": true,
"type": "number"
},
"day_id": {
"type": "number"
},
"start": {
"type": "date"
},
"end": {
"type": "date"
},
"business_id": {
"type": "number"
}
}
}

View File

@ -0,0 +1,21 @@
{
"name": "Time",
"base": "VnModel",
"options": {
"mysql": {
"table": "time"
}
},
"properties": {
"dated": {
"id": true,
"type": "date"
},
"year": {
"type": "number"
},
"week": {
"type": "number"
}
}
}

View File

@ -0,0 +1,18 @@
{
"name": "WorkerTimeControlConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "workerTimeControlConfig"
}
},
"properties": {
"id": {
"id": true,
"type": "number"
},
"timeToBreakTime": {
"type": "number"
}
}
}

View File

@ -1,3 +0,0 @@
module.exports = Self => {
require('../methods/worker-time-control-mail/checkInbox')(Self);
};

View File

@ -9,8 +9,7 @@
"properties": {
"id": {
"id": true,
"type": "number",
"required": true
"type": "number"
},
"workerFk": {
"type": "number"
@ -27,7 +26,7 @@
"updated": {
"type": "date"
},
"emailResponse": {
"reason": {
"type": "string"
}
},

View File

@ -5,6 +5,8 @@ module.exports = Self => {
require('../methods/worker-time-control/addTimeEntry')(Self);
require('../methods/worker-time-control/deleteTimeEntry')(Self);
require('../methods/worker-time-control/updateTimeEntry')(Self);
require('../methods/worker-time-control/sendMail')(Self);
require('../methods/worker-time-control/updateWorkerTimeControlMail')(Self);
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')

View File

@ -22,6 +22,9 @@
},
"direction": {
"type": "string"
},
"isSendMail": {
"type": "boolean"
}
},
"relations": {

View File

@ -77,6 +77,18 @@
</vn-tfoot>
</vn-table>
</vn-card>
<vn-button-bar class="vn-pa-xs vn-w-lg">
<vn-button
label="Satisfied"
ng-click="$ctrl.isSatisfied()">
</vn-button>
<vn-button
label="Not satisfied"
ng-click="reason.show()">
</vn-button>
</vn-button-bar>
<vn-side-menu side="right">
<div class="vn-pa-md">
<div class="totalBox" style="text-align: center;">
@ -148,4 +160,21 @@
ng-click="$ctrl.save()">
</vn-icon-button>
</vn-horizontal>
</vn-popover>
</vn-popover>
<vn-dialog
vn-id="reason"
on-accept="$ctrl.isUnsatisfied()">
<tpl-body>
<vn-textarea
label="Reason"
ng-model="$ctrl.reason"
required="true"
rows="3">
</vn-textarea>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>

View File

@ -294,6 +294,42 @@ class Controller extends Section {
this.$.editEntry.show($event);
}
getWeekNumber(currentDate) {
const startDate = new Date(currentDate.getFullYear(), 0, 1);
let days = Math.floor((currentDate - startDate) /
(24 * 60 * 60 * 1000));
return Math.ceil(days / 7);
}
isSatisfied() {
const weekNumber = this.getWeekNumber(this.date);
const params = {
workerId: this.worker.id,
year: this.date.getFullYear(),
week: weekNumber,
state: 'CONFIRMED'
};
const query = `WorkerTimeControls/updateWorkerTimeControlMail`;
this.$http.post(query, params).then(() => {
this.vnApp.showSuccess(this.$t('Data saved!'));
});
}
isUnsatisfied() {
const weekNumber = this.getWeekNumber(this.date);
const params = {
workerId: this.worker.id,
year: this.date.getFullYear(),
week: weekNumber,
state: 'REVISE',
reason: this.reason
};
const query = `WorkerTimeControls/updateWorkerTimeControlMail`;
this.$http.post(query, params).then(() => {
this.vnApp.showSuccess(this.$t('Data saved!'));
});
}
save() {
try {
const entry = this.selectedRow;

View File

@ -10,4 +10,7 @@ This time entry will be deleted: Se eliminará la hora fichada
Are you sure you want to delete this entry?: ¿Seguro que quieres eliminarla?
Finish at: Termina a las
Entry removed: Fichada borrada
The entry type can't be empty: El tipo de fichada no puede quedar vacía
The entry type can't be empty: El tipo de fichada no puede quedar vacía
Satisfied: Conforme
Not satisfied: No conforme
Reason: Motivo

40261
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -42,7 +42,7 @@
"puppeteer": "^18.0.5",
"read-chunk": "^3.2.0",
"require-yaml": "0.0.1",
"sharp": "^0.31.0",
"sharp": "^0.31.2",
"smbhash": "0.0.1",
"strong-error-handler": "^2.3.2",
"uuid": "^3.3.3",
@ -80,7 +80,7 @@
"html-loader-jest": "^0.2.1",
"html-webpack-plugin": "^4.0.0-beta.11",
"identity-obj-proxy": "^3.0.0",
"jasmine": "^4.1.0",
"jasmine": "^4.5.0",
"jasmine-reporters": "^2.4.0",
"jasmine-spec-reporter": "^7.0.0",
"jest": "^26.0.1",

View File

@ -0,0 +1,11 @@
const Stylesheet = require(`vn-print/core/stylesheet`);
const path = require('path');
const vnPrintPath = path.resolve('print');
module.exports = new Stylesheet([
`${vnPrintPath}/common/css/spacing.css`,
`${vnPrintPath}/common/css/misc.css`,
`${vnPrintPath}/common/css/layout.css`,
`${vnPrintPath}/common/css/email.css`])
.mergeStyles();

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width" />
<meta name="format-detection" content="telephone=no" />
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<slot name="header">
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
</slot>
<slot></slot>
<slot name="footer">
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
</slot>
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>

View File

@ -0,0 +1,12 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
module.exports = {
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build()
},
name: 'email-body',
};

View File

@ -3,7 +3,7 @@
<div class="buttons">
<div class="columns">
<div class="size50">
<a href="https://www.verdnatura.es" target="_blank">
<a href="https://verdnatura.es" target="_blank">
<div class="btn">
<!-- <span class="icon vn-pa-sm"><img v-bind:src="getEmailSrc('action.png')"/></span> -->
<span class="text vn-pa-sm">{{ $t('buttons.webAcccess')}}</span>

View File

@ -2,7 +2,7 @@ buttons:
webAcccess: Visit our website
info: Help us to improve
fiscalAddress: VERDNATURA LEVANTE SL, B97367486 C/ Fenollar, 2. 46680 ALGEMESI
· www.verdnatura.es · clientes@verdnatura.es
· verdnatura.es · clientes@verdnatura.es
disclaimer: '- NOTICE - This message is private and confidential, and should be used
exclusively by the person receiving it. If you have received this message by mistake,
please notify the sender and delete that message and any attached documents that it may contain.

View File

@ -2,7 +2,7 @@ buttons:
webAcccess: Visita nuestra Web
info: Ayúdanos a mejorar
fiscalAddress: VERDNATURA LEVANTE SL, B97367486 C/ Fenollar, 2. 46680 ALGEMESI
· www.verdnatura.es · clientes@verdnatura.es
· verdnatura.es · clientes@verdnatura.es
disclaimer: '- AVISO - Este mensaje es privado y confidencial, y debe ser utilizado
exclusivamente por la persona destinataria del mismo. Si has recibido este mensaje
por error, te rogamos lo comuniques al remitente y borres dicho mensaje y cualquier

View File

@ -2,7 +2,7 @@ buttons:
webAcccess: Visitez notre site web
info: Aidez-nous à améliorer
fiscalAddress: VERDNATURA LEVANTE SL, B97367486 C/ Fenollar, 2. 46680 ALGEMESI
· www.verdnatura.es · clientes@verdnatura.es
· verdnatura.es · clientes@verdnatura.es
disclaimer: "- AVIS - Ce message est privé et confidentiel et doit être utilisé
exclusivement par le destinataire. Si vous avez reçu ce message par erreur,
veuillez en informer l'expéditeur et supprimer ce message ainsi que tous les

View File

@ -2,7 +2,7 @@ buttons:
webAcccess: Visite o nosso site
info: Ajude-nos a melhorar
fiscalAddress: VERDNATURA LEVANTE SL, B97367486 C/ Fenollar, 2. 46680 ALGEMESI
· www.verdnatura.es · clientes@verdnatura.es
· verdnatura.es · clientes@verdnatura.es
disclaimer: '- AVISO - Esta mensagem é privada e confidencial e deve ser usada exclusivamente
pela pessoa que a recebe. Se você recebeu esta mensagem por engano, notifique o remetente e
exclua essa mensagem e todos os documentos anexos que ela possa conter.

View File

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

View File

@ -0,0 +1,10 @@
const Stylesheet = require(`vn-print/core/stylesheet`);
const path = require('path');
const vnPrintPath = path.resolve('print');
module.exports = new Stylesheet([
`${vnPrintPath}/common/css/layout.css`,
`${vnPrintPath}/common/css/report.css`,
`${vnPrintPath}/common/css/misc.css`])
.mergeStyles();

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<body>
<table class="grid">
<tbody>
<tr>
<td>
<slot name="header">
<report-header v-bind="$props"></report-header>
</slot>
<slot></slot>
<slot name="footer">
<report-footer id="pageFooter" v-bind="$props"></report-footer>
</slot>
</td>
</tr>
</tbody>
</table>
</body>
</html>

View File

@ -0,0 +1,12 @@
const Component = require(`vn-print/core/component`);
const reportHeader = new Component('report-header');
const reportFooter = new Component('report-footer');
module.exports = {
name: 'report-body',
components: {
'report-header': reportHeader.build(),
'report-footer': reportFooter.build()
},
};

View File

@ -1,2 +1,2 @@
company:
contactData: www.verdnatura.es - clientes@verdnatura.es
contactData: verdnatura.es - clientes@verdnatura.es

View File

@ -1,2 +1,2 @@
company:
contactData: www.verdnatura.es - clientes@verdnatura.es
contactData: verdnatura.es - clientes@verdnatura.es

View File

@ -1,2 +1,2 @@
company:
contactData: www.verdnatura.es - clientes@verdnatura.es
contactData: verdnatura.es - clientes@verdnatura.es

View File

@ -1,2 +1,2 @@
company:
contactData: · www.verdnatura.es · clientes@verdnatura.es
contactData: · verdnatura.es · clientes@verdnatura.es

View File

@ -32,7 +32,7 @@ class Email extends Component {
const rendered = await this.render();
const attachments = [];
const getAttachments = async(componentPath, files) => {
for (file of files) {
for (const file of files) {
const fileCopy = Object.assign({}, file);
const fileName = fileCopy.filename;
@ -54,15 +54,22 @@ class Email extends Component {
}
};
if (instance.components) {
const components = instance.components;
for (let componentName in components) {
const component = components[componentName];
const componentPath = `./components/${componentName}`;
await getAttachments(componentPath, component.attachments);
async function getSubcomponentAttachments(instance) {
if (instance.components) {
const components = instance.components;
for (let componentName in components) {
const component = components[componentName];
const componentPath = `./components/${componentName}`;
await getAttachments(componentPath, component.attachments);
if (component.components)
await getSubcomponentAttachments(component)
}
}
}
await getSubcomponentAttachments(instance)
if (this.attachments)
await getAttachments(this.path, this.attachments);

View File

@ -1,57 +1,17 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title v-html="$t('subject')"></title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<p>{{$t('description.instructions')}} {{client.name}}</p>
<p>{{$t('description.attached')}}</p>
<p>{{$t('description.response')}}</p>
<p>{{$t('description.regards')}}</p>
</div>
</div>
<!-- Preview block -->
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments"
v-bind:key="attachment.filename"
v-bind:attachment="attachment"
v-bind:args="$props">
</attachment>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<p>{{$t('description.instructions')}} {{client.name}}</p>
<p>{{$t('description.attached')}}</p>
<p>{{$t('description.response')}}</p>
<p>{{$t('description.regards')}}</p>
</div>
</div>
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments" v-bind:key="attachment.filename"
v-bind:attachment="attachment" v-bind:args="$props">
</attachment>
</div>
</div>
</email-body>

View File

@ -1,9 +1,7 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
const attachment = new Component('attachment');
module.exports = {
name: 'balance-compensation',
async serverPrefetch() {
@ -15,8 +13,7 @@ module.exports = {
},
},
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build(),
'email-body': emailBody.build(),
'attachment': attachment.build()
},
data() {

View File

@ -1,76 +1,37 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description', [dated])"></p>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<table class="column-oriented">
<thead>
<tr>
<th>{{$t('buyer')}}</th>
<th class="number">{{$t('percentage')}}</th>
<th class="number">{{$t('dwindle')}}</th>
<th class="number">{{$t('total')}}</th>
</tr>
</thead>
<tbody>
<tr v-for="waste in wastes" v-bind:key="waste.buyer">
<td class="font gray">{{waste.buyer}}</td>
<td class="number">{{(waste.percentage / 100) | percentage(2, 2, $i18n.locale)}}</td>
<td class="number">{{waste.dwindle | currency('EUR', $i18n.locale)}}</td>
<td class="number">{{waste.total | currency('EUR', $i18n.locale)}}</td>
</tr>
</tbody>
</table>
<p v-html="$t('wasteDetailLink')"></p>
<div class="external-link vn-pa-sm vn-m-md">
<a href="https://salix.verdnatura.es/#!/item/waste/index" target="_blank">
https://salix.verdnatura.es/#!/item/waste/index
</a>
</div>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description', [dated])"></p>
</div>
</div>
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<table class="column-oriented">
<thead>
<tr>
<th>{{$t('buyer')}}</th>
<th class="number">{{$t('percentage')}}</th>
<th class="number">{{$t('dwindle')}}</th>
<th class="number">{{$t('total')}}</th>
</tr>
</thead>
<tbody>
<tr v-for="waste in wastes" v-bind:key="waste.buyer">
<td class="font gray">{{waste.buyer}}</td>
<td class="number">{{(waste.percentage / 100) | percentage(2, 2, $i18n.locale)}}</td>
<td class="number">{{waste.dwindle | currency('EUR', $i18n.locale)}}</td>
<td class="number">{{waste.total | currency('EUR', $i18n.locale)}}</td>
</tr>
</tbody>
</table>
<p v-html="$t('wasteDetailLink')"></p>
<div class="external-link vn-pa-sm vn-m-md">
<a href="https://salix.verdnatura.es/#!/item/waste/index" target="_blank">
https://salix.verdnatura.es/#!/item/waste/index
</a>
</div>
</div>
</div>
</email-body>

View File

@ -1,6 +1,5 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
module.exports = {
name: 'buyer-week-waste',
@ -23,8 +22,7 @@ module.exports = {
}
},
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build()
'email-body': emailBody.build()
},
props: {}
};

View File

@ -1,46 +1,9 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description', [minDate, maxDate])"></p>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description', [minDate, maxDate])"></p>
</div>
</div>
</email-body>

View File

@ -1,6 +1,5 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
module.exports = {
name: 'campaign-metrics',
@ -16,8 +15,7 @@ module.exports = {
}
},
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build()
'email-body': emailBody.build(),
},
props: {
id: {

View File

@ -1,47 +1,10 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title', [id]) }}</h1>
<p>{{ $t('description.dear') }},</p>
<p v-html="instructions"></p>
<p>{{ $t('description.conclusion') }}</p>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title', [id]) }}</h1>
<p>{{ $t('description.dear') }},</p>
<p v-html="instructions"></p>
<p>{{ $t('description.conclusion') }}</p>
</div>
</div>
</email-body>

View File

@ -1,12 +1,10 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
module.exports = {
name: 'claim-pickup-order',
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build()
'email-body': emailBody.build(),
},
async serverPrefetch() {
this.ticket = await this.fetchTicket(this.id);

View File

@ -0,0 +1,6 @@
[
{
"filename": "client-debt-statement.pdf",
"component": "client-debt-statement"
}
]

View File

@ -1,55 +1,15 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('description.instructions')}}</p>
</div>
</div>
<!-- Preview block -->
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments"
v-bind:key="attachment.filename"
v-bind:attachment="attachment"
v-bind:args="$props">
</attachment>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('description.instructions')}}</p>
</div>
</div>
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments" v-bind:key="attachment.filename"
v-bind:attachment="attachment" v-bind:args="$props">
</attachment>
</div>
</div>
</email-body>

View File

@ -1,13 +1,11 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
const attachment = new Component('attachment');
module.exports = {
name: 'client-debt-statement',
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build(),
'email-body': emailBody.build(),
'attachment': attachment.build()
},
data() {

View File

@ -1,92 +1,56 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dearClient')}},</p>
<p v-html="$t('clientData')"></p>
<p>
<div>{{$t('clientId')}}: <strong>{{client.id}}</strong></div>
<div>{{$t('user')}}: <strong>{{client.userName}}</strong></div>
<div>{{$t('password')}}: <strong>********</strong>
(<a href="https://verdnatura.es">{{$t('passwordResetText')}}</a>)
</div>
</p>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dearClient')}},</p>
<p v-html="$t('clientData')"></p>
<h1>{{$t('sections.howToBuy.title')}}</h1>
<p>{{$t('sections.howToBuy.description')}}</p>
<ol>
<li v-for="requeriment in $t('sections.howToBuy.requeriments')">
<span v-html="requeriment"></span>
</li>
</ol>
<p>{{$t('sections.howToBuy.stock')}}</p>
<p>{{$t('sections.howToBuy.delivery')}}</p>
<h1>{{$t('sections.howToPay.title')}}</h1>
<p>{{$t('sections.howToPay.description')}}</p>
<ul>
<li v-for="option in $t('sections.howToPay.options')">
<span v-html="option"></span>
</li>
</ul>
<p>
<div>{{$t('clientId')}}: <strong>{{client.id}}</strong></div>
<div>{{$t('user')}}: <strong>{{client.userName}}</strong></div>
<div>{{$t('password')}}: <strong>********</strong>
(<a href="https://verdnatura.es">{{$t('passwordResetText')}}</a>)
</div>
</p>
<h1>{{$t('sections.toConsider.title')}}</h1>
<p>{{$t('sections.toConsider.description')}}</p>
<h1>{{$t('sections.howToBuy.title')}}</h1>
<p>{{$t('sections.howToBuy.description')}}</p>
<ol>
<li v-for="requeriment in $t('sections.howToBuy.requeriments')">
<span v-html="requeriment"></span>
</li>
</ol>
<p>{{$t('sections.howToBuy.stock')}}</p>
<p>{{$t('sections.howToBuy.delivery')}}</p>
<h4>{{$t('sections.claimsPolicy.title')}}</h4>
<p>{{$t('sections.claimsPolicy.description')}}</p>
<h1>{{$t('sections.howToPay.title')}}</h1>
<p>{{$t('sections.howToPay.description')}}</p>
<ul>
<li v-for="option in $t('sections.howToPay.options')">
<span v-html="option"></span>
</li>
</ul>
<p v-html="$t('help')"></p>
<p>
<section v-if="client.salesPersonName">
{{$t('salesPersonName')}}: <strong>{{client.salesPersonName}}</strong>
</section>
<section v-if="client.salesPersonPhone">
{{$t('salesPersonPhone')}}: <strong>{{client.salesPersonPhone}}</strong>
</section>
<section v-if="client.salesPersonEmail">
{{$t('salesPersonEmail')}}:
<strong><a v-bind:href="`mailto: ${client.salesPersonEmail}`" target="_blank">{{client.salesPersonEmail}}</strong>
</section>
</p>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<h1>{{$t('sections.toConsider.title')}}</h1>
<p>{{$t('sections.toConsider.description')}}</p>
<h4>{{$t('sections.claimsPolicy.title')}}</h4>
<p>{{$t('sections.claimsPolicy.description')}}</p>
<p v-html="$t('help')"></p>
<p>
<section v-if="client.salesPersonName">
{{$t('salesPersonName')}}: <strong>{{client.salesPersonName}}</strong>
</section>
<section v-if="client.salesPersonPhone">
{{$t('salesPersonPhone')}}: <strong>{{client.salesPersonPhone}}</strong>
</section>
<section v-if="client.salesPersonEmail">
{{$t('salesPersonEmail')}}:
<strong><a v-bind:href="`mailto: ${client.salesPersonEmail}`"
target="_blank">{{client.salesPersonEmail}}</strong>
</section>
</p>
</div>
</div>
</email-body>

View File

@ -1,6 +1,5 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
module.exports = {
name: 'client-welcome',
@ -13,8 +12,7 @@ module.exports = {
},
},
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build()
'email-body': emailBody.build(),
},
props: {
id: {

View File

@ -1,8 +1,8 @@
subject: Bienvenido a Verdnatura
title: "¡Te damos la bienvenida!"
dearClient: Estimado cliente
clientData: 'Tus datos para poder comprar en la web de Verdnatura (<a href="https://www.verdnatura.es"
title="Visitar Verdnatura" target="_blank" style="color: #8dba25">https://www.verdnatura.es</a>)
clientData: 'Tus datos para poder comprar en la web de Verdnatura (<a href="https://verdnatura.es"
title="Visitar Verdnatura" target="_blank" style="color: #8dba25">https://verdnatura.es</a>)
o en nuestras aplicaciones para <a href="https://goo.gl/3hC2mG" title="App Store"
target="_blank" style="color: #8dba25">iOS</a> y <a href="https://goo.gl/8obvLc"
title="Google Play" target="_blank" style="color: #8dba25">Android</a>, son'

View File

@ -1,55 +1,15 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('description.instructions')}}</p>
</div>
</div>
<!-- Preview block -->
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments"
v-bind:key="attachment.filename"
v-bind:attachment="attachment"
v-bind:args="$props">
</attachment>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('description.instructions')}}</p>
</div>
</div>
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments" v-bind:key="attachment.filename"
v-bind:attachment="attachment" v-bind:args="$props">
</attachment>
</div>
</div>
</email-body>

View File

@ -1,13 +1,11 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
const attachment = new Component('attachment');
module.exports = {
name: 'credit-request',
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build(),
'email-body': emailBody.build(),
'attachment': attachment.build()
},
data() {

View File

@ -2,4 +2,8 @@
border: 2px dashed #8dba25;
border-radius: 3px;
text-align: center
}
a {
color: #8dba25
}

View File

@ -1,69 +1,24 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
<style type="text/css">
a {
color: #8dba25
}
</style>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}}</p>
<p v-html="$t('description', [id])"></p>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-px-ml">
<p>{{$t('copyLink')}}</p>
<div class="external-link vn-pa-sm vn-m-md">
https://www.verdnatura.es/#!form=ecomerce/ticket&ticket={{id}}
</div>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<p v-html="$t('poll')"></p>
<p v-html="$t('help')"></p>
<p v-html="$t('conclusion')"></p>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}}</p>
<p v-html="$t('description', [id])"></p>
</div>
</div>
<div class="grid-row">
<div class="grid-block vn-px-ml">
<p>{{$t('copyLink')}}</p>
<div class="external-link vn-pa-sm vn-m-md">
https://shop.verdnatura.es/#!form=ecomerce/ticket&ticket={{id}}
</div>
</div>
</div>
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<p v-html="$t('poll')"></p>
<p v-html="$t('help')"></p>
<p v-html="$t('conclusion')"></p>
</div>
</div>
</email-body>

View File

@ -1,12 +1,10 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
module.exports = {
name: 'delivery-note-link',
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build()
'email-body': emailBody.build(),
},
props: {
id: {

View File

@ -2,7 +2,7 @@ subject: Your delivery note
title: Your delivery note
dear: Dear client
description: The delivery note from the order <strong>{0}</strong> is now available. <br/>
You can download it by clicking <a href="https://www.verdnatura.es/#!form=ecomerce/ticket&ticket={0}">this link</a>.
You can download it by clicking <a href="https://shop.verdnatura.es/#!form=ecomerce/ticket&ticket={0}">this link</a>.
copyLink: 'As an alternative, you can copy the following link in your browser:'
poll: If you wish, you can answer our satisfaction survey to
help us provide better service. Your opinion is very important for us!

View File

@ -2,7 +2,7 @@ subject: Tu albarán
title: Tu albarán
dear: Estimado cliente
description: Ya está disponible el albarán correspondiente al pedido <strong>{0}</strong>. <br/>
Puedes verlo haciendo clic <a href="https://www.verdnatura.es/#!form=ecomerce/ticket&ticket={0}">en este enlace</a>.
Puedes verlo haciendo clic <a href="https://shop.verdnatura.es/#!form=ecomerce/ticket&ticket={0}">en este enlace</a>.
copyLink: 'Como alternativa, puedes copiar el siguiente enlace en tu navegador:'
poll: Si lo deseas, puedes responder a nuestra encuesta de satisfacción para
ayudarnos a prestar un mejor servicio. ¡Tu opinión es muy importante para nosotros!

View File

@ -2,7 +2,7 @@ subject: Votre bon de livraison
title: Votre bon de livraison
dear: Cher client,
description: Le bon de livraison correspondant à la commande <strong>{0}</strong> est maintenant disponible.<br/>
Vous pouvez le voir en cliquant <a href="https://www.verdnatura.es/#!form=ecomerce/ticket&ticket={0}" target="_blank">sur ce lien</a>.
Vous pouvez le voir en cliquant <a href="https://shop.verdnatura.es/#!form=ecomerce/ticket&ticket={0}" target="_blank">sur ce lien</a>.
copyLink: 'Vous pouvez également copier le lien suivant dans votre navigateur:'
poll: Si vous le souhaitez, vous pouvez répondre à notre questionaire de satisfaction
pour nous aider à améliorer notre service. Votre avis est très important pour nous!

View File

@ -2,7 +2,7 @@ subject: Sua nota de entrega
title: Sua nota de entrega
dear: Estimado cliente
description: Já está disponível sua nota de entrega correspondente a encomenda numero <strong>{0}</strong>. <br/>
Para ver-lo faça um clique <a href="https://www.verdnatura.es/#!form=ecomerce/ticket&ticket={0}">neste link</a>.
Para ver-lo faça um clique <a href="https://shop.verdnatura.es/#!form=ecomerce/ticket&ticket={0}">neste link</a>.
copyLink: 'Como alternativa, podes copiar o siguinte link no teu navegador:'
poll: Si o deseja, podes responder nosso questionário de satiscação para ajudar-nos a prestar-vos um melhor serviço. Tua opinião é muito importante para nós!
help: Cualquer dúvida que surja, no hesites em consultar-la, <strong>Estamos aqui para

View File

@ -1,49 +1,12 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description', [id])"></p>
<p v-html="$t('poll')"></p>
<p v-html="$t('help')"></p>
<p v-html="$t('conclusion')"></p>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description', [id])"></p>
<p v-html="$t('poll')"></p>
<p v-html="$t('help')"></p>
<p v-html="$t('conclusion')"></p>
</div>
</div>
</email-body>

View File

@ -1,12 +1,10 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
module.exports = {
name: 'delivery-note',
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build()
'email-body': emailBody.build(),
},
props: {
id: {

View File

@ -1,45 +1,8 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('description.instructions')}}</p>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('description.instructions')}}</p>
</div>
</div>
</email-body>

View File

@ -1,12 +1,10 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
module.exports = {
name: 'driver-route',
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build()
'email-body': emailBody.build(),
},
props: {
id: {

View File

@ -1,57 +1,17 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('description.dear')}},</p>
<p>{{$t('description.instructions')}}</p>
<p>{{$t('description.conclusion')}}</p>
</div>
</div>
<!-- Attachments block -->
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments"
v-bind:key="attachment.filename"
v-bind:attachment="attachment"
v-bind:args="$props">
</attachment>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('description.dear')}},</p>
<p>{{$t('description.instructions')}}</p>
<p>{{$t('description.conclusion')}}</p>
</div>
</div>
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments" v-bind:key="attachment.filename"
v-bind:attachment="attachment" v-bind:args="$props">
</attachment>
</div>
</div>
</email-body>

View File

@ -1,6 +1,5 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
const attachment = new Component('attachment');
module.exports = {
@ -17,8 +16,7 @@ module.exports = {
};
},
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build(),
'email-body': emailBody.build(),
'attachment': attachment.build()
},
props: {

View File

@ -1,49 +1,12 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description', [invoice.ref, invoice.ticketFk])"></p>
<p v-html="$t('poll')"></p>
<p v-html="$t('help')"></p>
<p v-html="$t('conclusion')"></p>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description', [invoice.ref, invoice.ticketFk])"></p>
<p v-html="$t('poll')"></p>
<p v-html="$t('help')"></p>
<p v-html="$t('conclusion')"></p>
</div>
</div>
</email-body>

View File

@ -1,7 +1,5 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
module.exports = {
name: 'invoice',
async serverPrefetch() {
@ -13,8 +11,7 @@ module.exports = {
},
},
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build()
'email-body': emailBody.build(),
},
props: {
reference: {

View File

@ -1,47 +1,10 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description')"></p>
<p v-html="$t('conclusion')"></p>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description')"></p>
<p v-html="$t('conclusion')"></p>
</div>
</div>
</email-body>

View File

@ -1,11 +1,8 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
module.exports = {
name: 'invoiceIn',
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build()
'email-body': emailBody.build(),
}
};

View File

@ -1,88 +1,48 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{ $t('sections.introduction.title') }},</p>
<p>{{ $t('sections.introduction.description') }}</p>
<p>{{ $t('sections.introduction.terms') }}</p>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{ $t('sections.introduction.title') }},</p>
<p>{{ $t('sections.introduction.description') }}</p>
<p>{{ $t('sections.introduction.terms') }}</p>
<p>
{{ $t('sections.payMethod.description') }}:
<ol>
<li v-for="option in $t('sections.payMethod.options')">
{{ option }}
</li>
</ol>
</p>
<p>
{{ $t('sections.payMethod.description') }}:
<ol>
<li v-for="option in $t('sections.payMethod.options')">
{{ option }}
</li>
</ol>
</p>
<p>
{{ $t('sections.legalAction.description') }}:
<ol type="a">
<li v-for="option in $t('sections.legalAction.options')">
{{ option }}
</li>
</ol>
</p>
<p>
{{ $t('sections.legalAction.description') }}:
<ol type="a">
<li v-for="option in $t('sections.legalAction.options')">
{{ option }}
</li>
</ol>
</p>
<p v-html="$t('contactPhone')"></p>
<p v-html="$t('conclusion')"></p>
<p v-html="$t('contactPhone')"></p>
<p v-html="$t('conclusion')"></p>
<p>
<div class="row">
<div class="text">{{debtor.bankName}}</div>
<div class="control">{{debtor.iban}}</div>
<div class="description">
<div class="line"><span>{{$t('transferAccount') }}</span></div>
</div>
</div>
</p>
</div>
</div>
<!-- Block -->
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments"
v-bind:key="attachment.filename"
v-bind:attachment="attachment"
v-bind:args="$props">
</attachment>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<p>
<div class="row">
<div class="text">{{debtor.bankName}}</div>
<div class="control">{{debtor.iban}}</div>
<div class="description">
<div class="line"><span>{{$t('transferAccount') }}</span></div>
</div>
</div>
</p>
</div>
</div>
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments" v-bind:key="attachment.filename"
v-bind:attachment="attachment" v-bind:args="$props">
</attachment>
</div>
</div>
</email-body>

View File

@ -1,6 +1,5 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
const attachment = new Component('attachment');
module.exports = {
@ -28,8 +27,7 @@ module.exports = {
}
},
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build(),
'email-body': emailBody.build(),
'attachment': attachment.build()
},
props: {

View File

@ -1,71 +1,31 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{ $t('sections.introduction.title') }},</p>
<p>{{ $t('sections.introduction.description') }}</p>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{ $t('sections.introduction.title') }},</p>
<p>{{ $t('sections.introduction.description') }}</p>
<p>{{ $t('checkExtract') }}</p>
<p>{{ $t('checkValidData') }}</p>
<p>{{ $t('payMethod') }}</p>
<p>{{ $t('conclusion') }}</p>
<p>{{ $t('checkExtract') }}</p>
<p>{{ $t('checkValidData') }}</p>
<p>{{ $t('payMethod') }}</p>
<p>{{ $t('conclusion') }}</p>
<p>
<div class="row">
<div class="text">{{debtor.bankName}}</div>
<div class="control">{{debtor.iban}}</div>
<div class="description">
<div class="line"><span>{{$t('transferAccount') }}</span></div>
</div>
</div>
</p>
</div>
</div>
<!-- Block -->
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments"
v-bind:key="attachment.filename"
v-bind:attachment="attachment"
v-bind:args="$props">
</attachment>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<p>
<div class="row">
<div class="text">{{debtor.bankName}}</div>
<div class="control">{{debtor.iban}}</div>
<div class="description">
<div class="line"><span>{{$t('transferAccount') }}</span></div>
</div>
</div>
</p>
</div>
</div>
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments" v-bind:key="attachment.filename"
v-bind:attachment="attachment" v-bind:args="$props">
</attachment>
</div>
</div>
</email-body>

View File

@ -1,6 +1,5 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
const attachment = new Component('attachment');
module.exports = {
@ -28,8 +27,7 @@ module.exports = {
}
},
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build(),
'email-body': emailBody.build(),
'attachment': attachment.build()
},
props: {

View File

@ -1,99 +1,59 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description', [started, ended])"></p>
<p v-html="$t('totalResolved', [resolvedTickets])"></p>
<p v-html="$t('grafanaLink')"></p>
<div class="external-link vn-pa-sm vn-m-md">
<a v-bind:href="'https://grafana.verdnatura.es/d/2kaHDi9Mk/osticket?orgId=1&from=' + startedTime + '&to=' + endedTime" target="_blank">
https://grafana.verdnatura.es/d/2kaHDi9Mk/osticket?orgId=1&from={{startedTime}}&to={{endedTime}}
</a>
</div>
<p v-html="$t('redmineLink')"></p>
<div class="external-link vn-pa-sm vn-m-md">
<a href="https://redmine.verdnatura.es/projects/desarrollo/issues?query_id=112" target="_blank">
https://redmine.verdnatura.es/projects/desarrollo/issues?query_id=112
</a>
</div>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml" v-for="technician in technicians">
<div class="table-title clearfix">
<h2>{{technician.name}} (<strong>{{technician.tickets.length}}</strong>)</h2>
</div>
<table class="column-oriented">
<thead>
<tr>
<th width="5%">{{$t('author')}}</th>
<th width="15%">{{$t('dated')}}</th>
<th width="25%">{{$t('ticketSubject')}}</th>
<th width="30%">{{$t('ticketDescription')}}</th>
<th width="20%">{{$t('resolution')}}</th>
</tr>
</thead>
<tbody v-for="ticket in technician.tickets">
<tr>
<td>{{ticket.author}}</td>
<td class="font light-gray">
<div v-bind:title="$t('opened')">
&#128275; {{ticket.created | date('%d-%m-%Y %H:%M')}}
</div>
<div v-bind:title="$t('closed')">
&#128274; {{ticket.closed | date('%d-%m-%Y %H:%M')}}
</div>
</td>
<td>
<a v-bind:href="'https://cau.verdnatura.es/scp/tickets.php?id=' + ticket.ticket_id">
{{ticket.number}} - {{ticket.subject}}
</a>
</td>
<td class="message" v-html="ticket.description"></td>
<td class="message" v-html="ticket.resolution"></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description', [started, ended])"></p>
<p v-html="$t('totalResolved', [resolvedTickets])"></p>
<p v-html="$t('grafanaLink')"></p>
<div class="external-link vn-pa-sm vn-m-md">
<a
v-bind:href="'https://grafana.verdnatura.es/d/2kaHDi9Mk/osticket?orgId=1&from=' + startedTime + '&to=' + endedTime"
target="_blank"
>
https://grafana.verdnatura.es/d/2kaHDi9Mk/osticket?orgId=1&from={{startedTime}}&to={{endedTime}}
</a>
</div>
<p v-html="$t('redmineLink')"></p>
<div class="external-link vn-pa-sm vn-m-md">
<a href="https://redmine.verdnatura.es/projects/desarrollo/issues?query_id=112" target="_blank">
https://redmine.verdnatura.es/projects/desarrollo/issues?query_id=112
</a>
</div>
</div>
</div>
<div class="grid-row">
<div class="grid-block vn-pa-ml" v-for="technician in technicians">
<div class="table-title clearfix">
<h2>{{technician.name}} (<strong>{{technician.tickets.length}}</strong>)</h2>
</div>
<table class="column-oriented">
<thead>
<tr>
<th width="5%">{{$t('author')}}</th>
<th width="15%">{{$t('dated')}}</th>
<th width="25%">{{$t('ticketSubject')}}</th>
<th width="30%">{{$t('ticketDescription')}}</th>
<th width="20%">{{$t('resolution')}}</th>
</tr>
</thead>
<tbody v-for="ticket in technician.tickets">
<tr>
<td>{{ticket.author}}</td>
<td class="font light-gray">
<div v-bind:title="$t('opened')">&#128275; {{ticket.created | date('%d-%m-%Y %H:%M')}}</div>
<div v-bind:title="$t('closed')">&#128274; {{ticket.closed | date('%d-%m-%Y %H:%M')}}</div>
</td>
<td>
<a v-bind:href="'https://cau.verdnatura.es/scp/tickets.php?id=' + ticket.ticket_id">
{{ticket.number}} - {{ticket.subject}}
</a>
</td>
<td class="message" v-html="ticket.description"></td>
<td class="message" v-html="ticket.resolution"></td>
</tr>
</tbody>
</table>
</div>
</div>
</email-body>

View File

@ -1,6 +1,5 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
module.exports = {
name: 'osticket-report',
@ -61,8 +60,7 @@ module.exports = {
}
},
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build()
'email-body': emailBody.build(),
},
props: {}
};

View File

@ -1,66 +1,28 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{ $t('sections.introduction.title') }},</p>
<p v-html="`${$t('sections.introduction.description')}:`"></p>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{ $t('sections.introduction.title') }},</p>
<p v-html="`${$t('sections.introduction.description')}:`"></p>
<p>
<section>
<span>{{ $t('sections.pay.method') }}:</span>
<strong>{{payMethod.name}}</strong>
</section>
<section v-if="payMethod.code != 'card'">
<span>{{ $t('sections.pay.day') }}:</span>
<strong>{{ $t('sections.pay.dueDay', [payMethod.dueDay]) }}</strong>
</section>
</p>
<p>
<section>
<span>{{ $t('sections.pay.method') }}:</span>
<strong>{{payMethod.name}}</strong>
</section>
<section v-if="payMethod.code != 'card'">
<span>{{ $t('sections.pay.day') }}:</span>
<strong>{{ $t('sections.pay.dueDay', [payMethod.dueDay]) }}</strong>
</section>
</p>
<p v-if="payMethod.code == 'bankDraft'"
v-html="$t('sections.pay.accountImplicates', [accountAddress])">
</p>
<p v-else-if="payMethod.code == 'card'">
{{ $t('sections.pay.cardImplicates') }}
</p>
<p v-if="payMethod.code == 'bankDraft'" v-html="$t('sections.pay.accountImplicates', [accountAddress])">
</p>
<p v-else-if="payMethod.code == 'card'">
{{ $t('sections.pay.cardImplicates') }}
</p>
<p>{{ $t('notifyAnError') }}</p>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<p>{{ $t('notifyAnError') }}</p>
</div>
</div>
</email-body>

View File

@ -1,6 +1,5 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
module.exports = {
name: 'payment-update',
@ -21,8 +20,7 @@ module.exports = {
}
},
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build()
'email-body': emailBody.build(),
},
props: {
id: {

View File

@ -1,90 +1,49 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('description.dear')}},</p>
<p>{{$t('description.instructions')}}</p>
<p v-html="$t('description.followGuide')"></p>
<p v-html="$t('description.downloadFrom')"></p>
<p v-html="$t('description.downloadDriver')"></p>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('description.dear')}},</p>
<p>{{$t('description.instructions')}}</p>
<p v-html="$t('description.followGuide')"></p>
<p v-html="$t('description.downloadFrom')"></p>
<p v-html="$t('description.downloadDriver')"></p>
<h1>{{$t('sections.QLabel.title')}}</h1>
<p>{{$t('sections.QLabel.description')}}:</p>
<ol>
<li v-for="step in $t('sections.QLabel.steps')">
<span v-html="step"></span>
</li>
</ol>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{$t('sections.help.title')}}</h1>
<p>{{$t('sections.help.description')}}</p>
<p v-html="$t('sections.help.remoteSupport')"></p>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<div v-if="client.salesPersonName">
{{$t('salesPersonName')}}: <strong>{{client.salesPersonName}}</strong>
</div>
<div v-if="client.salesPersonPhone">
{{$t('salesPersonPhone')}}: <strong>{{client.salesPersonPhone}}</strong>
</div>
<div v-if="client.salesPersonEmail">
{{$t('salesPersonEmail')}}:
<strong><a v-bind:href="`mailto:${client.salesPersonEmail}`" target="_blank">{{client.salesPersonEmail}}</strong>
</div>
</div>
</div>
<!-- Block -->
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments"
v-bind:key="attachment.filename"
v-bind:attachment="attachment"
v-bind:args="$props">
</attachment>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<h1>{{$t('sections.QLabel.title')}}</h1>
<p>{{$t('sections.QLabel.description')}}:</p>
<ol>
<li v-for="step in $t('sections.QLabel.steps')">
<span v-html="step"></span>
</li>
</ol>
</div>
</div>
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{$t('sections.help.title')}}</h1>
<p>{{$t('sections.help.description')}}</p>
<p v-html="$t('sections.help.remoteSupport')"></p>
</div>
</div>
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<div v-if="client.salesPersonName">
{{$t('salesPersonName')}}: <strong>{{client.salesPersonName}}</strong>
</div>
<div v-if="client.salesPersonPhone">
{{$t('salesPersonPhone')}}: <strong>{{client.salesPersonPhone}}</strong>
</div>
<div v-if="client.salesPersonEmail">
{{$t('salesPersonEmail')}}:
<strong><a v-bind:href="`mailto:${client.salesPersonEmail}`"
target="_blank">{{client.salesPersonEmail}}</strong>
</div>
</div>
</div>
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments" v-bind:key="attachment.filename"
v-bind:attachment="attachment" v-bind:args="$props">
</attachment>
</div>
</div>
</email-body>

View File

@ -1,6 +1,5 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
const attachment = new Component('attachment');
const attachments = require('./attachments.json');
@ -18,8 +17,7 @@ module.exports = {
}
},
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build(),
'email-body': emailBody.build(),
'attachment': attachment.build()
},
props: {

View File

@ -1,57 +1,21 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('description.dear')}},</p>
<div v-html="$t('description.instructions')"></div>
<p>{{$t('description.conclusion')}}</p>
</div>
</div>
<!-- Attachments block -->
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment v-for="attachment in attachments"
v-bind:key="attachment.filename"
v-bind:attachment="attachment"
v-bind:args="$props">
</attachment>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('description.dear')}},</p>
<div v-html="$t('description.instructions')"></div>
<p>{{$t('description.conclusion')}}</p>
</div>
</div>
<div class="grid-row" v-if="isPreview">
<div class="grid-block vn-pa-ml">
<attachment
v-for="attachment in attachments"
v-bind:key="attachment.filename"
v-bind:attachment="attachment"
v-bind:args="$props"
>
</attachment>
</div>
</div>
</email-body>

View File

@ -1,7 +1,5 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const attachment = new Component('attachment');
const emailBody = new Component('email-body');
const attachments = require('./attachments.json');
module.exports = {
@ -10,9 +8,7 @@ module.exports = {
return {attachments};
},
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build(),
'attachment': attachment.build()
'email-body': emailBody.build()
},
props: {
id: {

View File

@ -1,46 +1,9 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<head>
<meta name="viewport" content="width=device-width">
<meta name="format-detection" content="telephone=no">
<title>{{ $t('subject') }}</title>
</head>
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
<!-- Header block -->
<div class="grid-row">
<div class="grid-block">
<email-header v-bind="$props"></email-header>
</div>
</div>
<!-- Block -->
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description', [minDate, maxDate])"></p>
</div>
</div>
<!-- Footer block -->
<div class="grid-row">
<div class="grid-block">
<email-footer v-bind="$props"></email-footer>
</div>
</div>
<!-- Empty block -->
<div class="grid-row">
<div class="grid-block empty"></div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description', [minDate, maxDate])"></p>
</div>
</div>
</email-body>

View File

@ -1,6 +1,5 @@
const Component = require(`vn-print/core/component`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const emailBody = new Component('email-body');
module.exports = {
name: 'supplier-campaign-metrics',
@ -16,8 +15,7 @@ module.exports = {
}
},
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build()
'email-body': emailBody.build()
},
props: {
id: {

View File

@ -1,41 +1,31 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Header block -->
<report-header v-bind="$props">
</report-header>
<div class="grid-row">
<div class="grid-block">
<div class="columns">
<div class="size50">
<p style="text-align: right;">{{$t('Place')}} {{currentDate()}}</p>
<h3 style="text-align: center; margin-top: 8%;">{{$t('Compensation') | uppercase}}</h3>
<p style="margin-top: 8%;">{{$t('In one hand')}}:</p>
<p style="text-align: justify;">{{company.name}} {{$t('CIF')}} {{company.nif}} {{$t('Home')}} {{company.street}}, {{company.city}}.</p>
<p style="margin-top: 5%;">{{$t('In other hand')}}:</p>
<p style="text-align: justify;">{{$t('Sr')}} {{client.name}} {{$t('NIF')}} {{client.fi}} {{$t('Home')}} {{client.street}}, {{client.city}}.</p>
<h4 style="text-align: center;margin-top: 10%;">{{$t('Agree') | uppercase}}</h4>
<p style="margin-top: 8%;text-align: justify;">{{$t('Date')}} {{client.payed | date('%d-%m-%Y')}} {{$t('Compensate')}} {{client.amountPaid}} € {{$t('From client')}} {{client.name}} {{$t('To client')}} {{company.name}}.</p>
<p style="margin-top: 8%;">{{$t('Reception')}} <span style="color:blue">administracion@verdnatura.es</span></p>
<div style="margin-top: 8%;"><small >{{$t('Greetings')}}</small></div>
</div>
</div>
</div>
</div>
<!-- Footer block -->
<report-footer id="pageFooter"
v-bind="$props">
</report-footer>
</td>
</tr>
</tbody>
</table>
</body>
</html>
<report-body v-bind="$props">
<div class="grid-row">
<div class="grid-block">
<div class="columns">
<div class="size50">
<p style="text-align: right">{{$t('Place')}} {{currentDate()}}</p>
<h3 style="text-align: center; margin-top: 8%">{{$t('Compensation') | uppercase}}</h3>
<p style="margin-top: 8%">{{$t('In one hand')}}:</p>
<p style="text-align: justify">
{{company.name}} {{$t('CIF')}} {{company.nif}} {{$t('Home')}} {{company.street}},
{{company.city}}.
</p>
<p style="margin-top: 5%">{{$t('In other hand')}}:</p>
<p style="text-align: justify">
{{$t('Sr')}} {{client.name}} {{$t('NIF')}} {{client.fi}} {{$t('Home')}} {{client.street}},
{{client.city}}.
</p>
<h4 style="text-align: center; margin-top: 10%">{{$t('Agree') | uppercase}}</h4>
<p style="margin-top: 8%; text-align: justify">
{{$t('Date')}} {{client.payed | date('%d-%m-%Y')}} {{$t('Compensate')}} {{client.amountPaid}} €
{{$t('From client')}} {{client.name}} {{$t('Toclient')}} {{company.name}}.
</p>
<p style="margin-top: 8%">
{{$t('Reception')}} <span style="color: blue">administracion@verdnatura.es</span>
</p>
<div style="margin-top: 8%"><small>{{$t('Greetings')}}</small></div>
</div>
</div>
</div>
</div>
</report-body>

View File

@ -1,13 +1,12 @@
const Component = require(`vn-print/core/component`);
const reportHeader = new Component('report-header');
const reportFooter = new Component('report-footer');
const reportBody = new Component('report-body');
module.exports = {
name: 'balance-compensation',
async serverPrefetch() {
this.client = await this.fetchClient(this.id);
this.company = await this.fetchCompany(this.id);
},
},
methods: {
fetchClient(id) {
return this.findOneFromDef('client', [id]);
@ -15,16 +14,15 @@ module.exports = {
fetchCompany(id) {
return this.findOneFromDef('company', [id]);
},
currentDate() {
const current = new Date();
const date = `${current.getDate()}/${current.getMonth()+1}/${current.getFullYear()}`;
const date = `${current.getDate()}/${current.getMonth() + 1}/${current.getFullYear()}`;
return date;
}
}
},
components: {
'report-header': reportHeader.build(),
'report-footer': reportFooter.build()
'report-body': reportBody.build()
},
props: {
id: {

View File

@ -1,95 +1,72 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Header block -->
<report-header v-bind="$props"></report-header>
<!-- Block -->
<div class="grid-row">
<div class="grid-block">
<div class="columns">
<div class="size50">
<h1 class="title uppercase">{{$t('title')}}</h1>
<div class="size75">
<table class="row-oriented report-info">
<tbody>
<tr>
<td class="font gray">{{$t('Client')}}</td>
<th>{{client.id}}</th>
</tr>
<tr>
<td class="font gray">{{$t('From')}}</td>
<th>{{from | date('%d-%m-%Y')}}</th>
</tr>
<tr>
<td class="font gray">{{$t('To')}}</td>
<th>{{to | date('%d-%m-%Y')}}</th>
</tr>
</tbody>
</table>
</div>
</div>
<div class="size50">
<div class="panel">
<div class="header">{{$t('clientData')}}</div>
<div class="body">
<h3 class="uppercase">{{client.socialName}}</h3>
<div>
{{client.street}}
</div>
<div>
{{client.postcode}}, {{client.city}} ({{client.province}})
</div>
<div>
{{client.country}}
</div>
</div>
</div>
</div>
</div>
<table class="column-oriented">
<thead>
<tr>
<th>{{$t('Code')}}</th>
<th class="number">{{$t('Quantity')}}</th>
<th width="50%">{{$t('Concept')}}</th>
</tr>
</thead>
<tbody v-for="sale in sales">
<tr>
<td>{{sale.itemFk | zerofill('000000')}}</td>
<td class="number">{{Math.trunc(sale.subtotal)}}</td>
<td width="50%">{{sale.concept}}</td>
</tr>
<tr class="description font light-gray">
<td colspan="7">
<span v-if="sale.value5">
<strong>{{sale.tag5}}</strong> {{sale.value5}}
</span>
<span v-if="sale.value6">
<strong>{{sale.tag6}}</strong> {{sale.value6}}
</span>
<span v-if="sale.value7">
<strong>{{sale.tag7}}</strong> {{sale.value7}}
</span>
</td>
</tr>
</tbody>
</table>
</div>
<report-body v-bind="$props">
<div class="grid-row">
<div class="grid-block">
<div class="columns">
<div class="size50">
<h1 class="title uppercase">{{$t('title')}}</h1>
<div class="size75">
<table class="row-oriented report-info">
<tbody>
<tr>
<td class="font gray">{{$t('Client')}}</td>
<th>{{client.id}}</th>
</tr>
<tr>
<td class="font gray">{{$t('From')}}</td>
<th>{{from | date('%d-%m-%Y')}}</th>
</tr>
<tr>
<td class="font gray">{{$t('To')}}</td>
<th>{{to | date('%d-%m-%Y')}}</th>
</tr>
</tbody>
</table>
</div>
</div>
<div class="size50">
<div class="panel">
<div class="header">{{$t('clientData')}}</div>
<div class="body">
<h3 class="uppercase">{{client.socialName}}</h3>
<div>{{client.street}}</div>
<div>{{client.postcode}}, {{client.city}} ({{client.province}})</div>
<div>{{client.country}}</div>
</div>
<!-- Footer block -->
<report-footer id="pageFooter"
v-bind:left-text="$t('client', [client.id])"
v-bind:center-text="client.socialName"
v-bind="$props">
</report-footer>
</td>
</tr>
</tbody>
</table>
</body>
</html>
</div>
</div>
</div>
<table class="column-oriented">
<thead>
<tr>
<th>{{$t('Code')}}</th>
<th class="number">{{$t('Quantity')}}</th>
<th width="50%">{{$t('Concept')}}</th>
</tr>
</thead>
<tbody v-for="sale in sales">
<tr>
<td>{{sale.itemFk | zerofill('000000')}}</td>
<td class="number">{{Math.trunc(sale.subtotal)}}</td>
<td width="50%">{{sale.concept}}</td>
</tr>
<tr class="description font light-gray">
<td colspan="7">
<span v-if="sale.value5"> <strong>{{sale.tag5}}</strong> {{sale.value5}} </span>
<span v-if="sale.value6"> <strong>{{sale.tag6}}</strong> {{sale.value6}} </span>
<span v-if="sale.value7"> <strong>{{sale.tag7}}</strong> {{sale.value7}} </span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<template v-slot:footer>
<report-footer
id="pageFooter"
v-bind:left-text="$t('client', [client.id])"
v-bind:center-text="client.socialName"
v-bind="$props"
>
</report-footer>
</template>
</report-body>

View File

@ -1,5 +1,5 @@
const Component = require(`vn-print/core/component`);
const reportHeader = new Component('report-header');
const reportBody = new Component('report-body');
const reportFooter = new Component('report-footer');
module.exports = {
@ -20,7 +20,7 @@ module.exports = {
},
},
components: {
'report-header': reportHeader.build(),
'report-body': reportBody.build(),
'report-footer': reportFooter.build()
},
props: {

View File

@ -1,94 +1,77 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Header block -->
<report-header v-bind="$props"></report-header>
<!-- Block -->
<div class="grid-row">
<div class="grid-block">
<div class="columns">
<div class="size50">
<h1 class="title uppercase">{{$t('title')}}</h1>
<table class="row-oriented">
<tbody>
<tr>
<td class="font gray uppercase">{{$t('claimId')}}</td>
<th>{{id}}</th>
</tr>
<tr>
<td class="font gray uppercase">{{$t('clientId')}}</td>
<th>{{client.id}}</th>
</tr>
<tr>
<td class="font gray uppercase">{{$t('phone')}}</td>
<th>{{client.phone}}</th>
</tr>
<tr>
<td class="font gray uppercase">{{$t('date')}}</td>
<th>{{dated}}</th>
</tr>
</tbody>
</table>
</div>
<div class="size50">
<div class="panel">
<div class="header">{{$t('clientData')}}</div>
<div class="body">
<h3 class="uppercase">{{client.nickname}}</h3>
<div>
{{client.street}}
</div>
<div>
{{client.postalCode}}, {{client.city}} ({{client.province}})
</div>
<div>
{{client.country}}
</div>
</div>
</div>
</div>
</div>
<table class="column-oriented vn-mt-ml">
<thead>
<tr>
<th>{{$t('reference')}}</th>
<th class="number">{{$t('quantity')}}</th>
<th class="number">{{$t('claims')}}</th>
<th width="50%">{{$t('concept')}}</th>
</tr>
</thead>
<tbody v-for="sale in sales" v-bind:key="sale.id">
<tr>
<td class="font gray">{{sale.id}}</td>
<td class="number">{{sale.quantity}}</td>
<td class="number">{{sale.claimQuantity}}</td>
<td width="50%">{{sale.concept}}</td>
</tr>
</tbody>
</table>
<div class="panel sign">
<div class="header">{{$t('clientSignature')}}</div>
<div class="body centered">
<h3>{{client.name}}</h3>
</div>
</div>
</div>
<report-body v-bind="$props">
<div class="grid-row">
<div class="grid-block">
<div class="columns">
<div class="size50">
<h1 class="title uppercase">{{$t('title')}}</h1>
<table class="row-oriented">
<tbody>
<tr>
<td class="font gray uppercase">{{$t('claimId')}}</td>
<th>{{id}}</th>
</tr>
<tr>
<td class="font gray uppercase">{{$t('clientId')}}</td>
<th>{{client.id}}</th>
</tr>
<tr>
<td class="font gray uppercase">{{$t('phone')}}</td>
<th>{{client.phone}}</th>
</tr>
<tr>
<td class="font gray uppercase">{{$t('date')}}</td>
<th>{{dated}}</th>
</tr>
</tbody>
</table>
</div>
<div class="size50">
<div class="panel">
<div class="header">{{$t('clientData')}}</div>
<div class="body">
<h3 class="uppercase">{{client.nickname}}</h3>
<div>{{client.street}}</div>
<div>{{client.postalCode}}, {{client.city}} ({{client.province}})</div>
<div>{{client.country}}</div>
</div>
<!-- Footer block -->
<report-footer id="pageFooter"
v-bind:left-text="$t('claim', [id])"
v-bind:center-text="client.name"
v-bind="$props">
</report-footer>
</td>
</tr>
</tbody>
</table>
</body>
</html>
</div>
</div>
</div>
<table class="column-oriented vn-mt-ml">
<thead>
<tr>
<th>{{$t('reference')}}</th>
<th class="number">{{$t('quantity')}}</th>
<th class="number">{{$t('claims')}}</th>
<th width="50%">{{$t('concept')}}</th>
</tr>
</thead>
<tbody v-for="sale in sales" v-bind:key="sale.id">
<tr>
<td class="font gray">{{sale.id}}</td>
<td class="number">{{sale.quantity}}</td>
<td class="number">{{sale.claimQuantity}}</td>
<td width="50%">{{sale.concept}}</td>
</tr>
</tbody>
</table>
<div class="panel sign">
<div class="header">{{$t('clientSignature')}}</div>
<div class="body centered">
<h3>{{client.name}}</h3>
</div>
</div>
</div>
</div>
<template v-slot:footer>
<report-footer
id="pageFooter"
v-bind:left-text="$t('claim', [id])"
v-bind:center-text="client.name"
v-bind="$props"
>
</report-footer>
</template>
</report-body>

View File

@ -1,5 +1,5 @@
const Component = require(`vn-print/core/component`);
const reportHeader = new Component('report-header');
const reportBody = new Component('report-body');
const reportFooter = new Component('report-footer');
module.exports = {
@ -27,7 +27,7 @@ module.exports = {
}
},
components: {
'report-header': reportHeader.build(),
'report-body': reportBody.build(),
'report-footer': reportFooter.build()
},
props: {

View File

@ -1,95 +1,78 @@
<!DOCTYPE html>
<html v-bind:lang="$i18n.locale">
<body>
<table class="grid">
<tbody>
<tr>
<td>
<!-- Header block -->
<report-header v-bind="$props"></report-header>
<!-- Block -->
<div class="grid-row">
<div class="grid-block">
<div class="columns">
<div class="size50">
<div class="size75">
<h1 class="title uppercase">{{$t('title')}}</h1>
<table class="row-oriented">
<tbody>
<tr>
<td class="font gray uppercase">{{$t('clientId')}}</td>
<th>{{client.id}}</th>
</tr>
<tr>
<td class="font gray uppercase">{{$t('date')}}</td>
<th>{{dated}}</th>
</tr>
</tbody>
</table>
</div>
</div>
<div class="size50">
<div class="panel">
<div class="header">{{$t('clientData')}}</div>
<div class="body">
<h3 class="uppercase">{{client.socialName}}</h3>
<div>
{{client.street}}
</div>
<div>
{{client.postcode}}, {{client.city}} ({{client.province}})
</div>
<div>
{{client.country}}
</div>
</div>
</div>
</div>
</div>
<table class="column-oriented">
<thead>
<tr>
<th>{{$t('date')}}</th>
<th>{{$t('concept')}}</th>
<th class="number">{{$t('invoiced')}}</th>
<th class="number">{{$t('payed')}}</th>
<th class="number">{{$t('balance')}}</th>
</tr>
</thead>
<tbody v-for="sale in sales" :key="sale.id">
<tr>
<td>{{sale.issued | date('%d-%m-%Y')}}</td>
<td>{{sale.ref}}</td>
<td class="number">{{sale.debtOut}}</td>
<td class="number">{{sale.debtIn}}</td>
<td class="number">{{getBalance(sale)}}</td>
</tr>
</tbody>
<tfoot>
<tr>
<td></td>
<td></td>
<td class="number">
<strong class="pull-left">Total</strong>
{{getTotalDebtOut() | currency('EUR', $i18n.locale)}}
</td>
<td class="number">{{getTotalDebtIn() | currency('EUR', $i18n.locale)}}</td>
<td class="number">{{totalBalance | currency('EUR', $i18n.locale)}}</td>
</tr>
</tfoot>
</table>
</div>
<report-body v-bind="$props">
<div class="grid-row">
<div class="grid-block">
<div class="columns">
<div class="size50">
<div class="size75">
<h1 class="title uppercase">{{$t('title')}}</h1>
<table class="row-oriented">
<tbody>
<tr>
<td class="font gray uppercase">{{$t('clientId')}}</td>
<th>{{client.id}}</th>
</tr>
<tr>
<td class="font gray uppercase">{{$t('date')}}</td>
<th>{{dated}}</th>
</tr>
</tbody>
</table>
</div>
</div>
<div class="size50">
<div class="panel">
<div class="header">{{$t('clientData')}}</div>
<div class="body">
<h3 class="uppercase">{{client.socialName}}</h3>
<div>{{client.street}}</div>
<div>{{client.postcode}}, {{client.city}} ({{client.province}})</div>
<div>{{client.country}}</div>
</div>
<!-- Footer block -->
<report-footer id="pageFooter"
v-bind:left-text="$t('client', [client.id])"
v-bind:center-text="client.socialName"
v-bind="$props">
</report-footer>
</td>
</tr>
</tbody>
</table>
</body>
</html>
</div>
</div>
</div>
<table class="column-oriented">
<thead>
<tr>
<th>{{$t('date')}}</th>
<th>{{$t('concept')}}</th>
<th class="number">{{$t('invoiced')}}</th>
<th class="number">{{$t('payed')}}</th>
<th class="number">{{$t('balance')}}</th>
</tr>
</thead>
<tbody v-for="sale in sales" :key="sale.id">
<tr>
<td>{{sale.issued | date('%d-%m-%Y')}}</td>
<td>{{sale.ref}}</td>
<td class="number">{{sale.debtOut}}</td>
<td class="number">{{sale.debtIn}}</td>
<td class="number">{{getBalance(sale)}}</td>
</tr>
</tbody>
<tfoot>
<tr>
<td></td>
<td></td>
<td class="number">
<strong class="pull-left">Total</strong>
{{getTotalDebtOut() | currency('EUR', $i18n.locale)}}
</td>
<td class="number">{{getTotalDebtIn() | currency('EUR', $i18n.locale)}}</td>
<td class="number">{{totalBalance | currency('EUR', $i18n.locale)}}</td>
</tr>
</tfoot>
</table>
</div>
</div>
<template v-slot:footer>
<report-footer
id="pageFooter"
v-bind:left-text="$t('client', [client.id])"
v-bind:center-text="client.socialName"
v-bind="$props"
>
</report-footer>
</template>
</report-body>

View File

@ -1,5 +1,5 @@
const Component = require(`vn-print/core/component`);
const reportHeader = new Component('report-header');
const reportBody = new Component('report-body');
const reportFooter = new Component('report-footer');
module.exports = {
@ -64,7 +64,7 @@ module.exports = {
},
},
components: {
'report-header': reportHeader.build(),
'report-body': reportBody.build(),
'report-footer': reportFooter.build()
},
props: {

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