diff --git a/back/methods/dms/deleteTrashFiles.js b/back/methods/dms/deleteTrashFiles.js
index 63d7021c5..f14e65e9f 100644
--- a/back/methods/dms/deleteTrashFiles.js
+++ b/back/methods/dms/deleteTrashFiles.js
@@ -51,7 +51,7 @@ module.exports = Self => {
const dstFile = path.join(dmsContainer.client.root, pathHash, dms.file);
await fs.unlink(dstFile);
} catch (err) {
- if (err.code != 'ENOENT')
+ if (err.code != 'ENOENT' && dms.file)
throw err;
}
diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index f41d1eb53..5fa2d1655 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -2569,10 +2569,6 @@ UPDATE `vn`.`route`
UPDATE `vn`.`route`
SET `invoiceInFk`=2
WHERE `id`=2;
-INSERT INTO `bs`.`salesPerson` (`workerFk`, `year`, `month`)
- VALUES
- (18, YEAR(util.VN_CURDATE()), MONTH(util.VN_CURDATE())),
- (19, YEAR(util.VN_CURDATE()), MONTH(util.VN_CURDATE()));
INSERT INTO `bs`.`sale` (`saleFk`, `amount`, `dated`, `typeFk`, `clientFk`)
VALUES
diff --git a/front/salix/components/log/index.js b/front/salix/components/log/index.js
index 2796931a0..f30878b9f 100644
--- a/front/salix/components/log/index.js
+++ b/front/salix/components/log/index.js
@@ -37,7 +37,7 @@ export default class Controller extends Section {
const validations = window.validations;
value.forEach(log => {
- const locale = validations[log.changedModel].locale ? validations[log.changedModel].locale : {};
+ const locale = validations[log.changedModel] && validations[log.changedModel].locale ? validations[log.changedModel].locale : {};
log.oldProperties = this.getInstance(log.oldInstance, locale);
log.newProperties = this.getInstance(log.newInstance, locale);
diff --git a/modules/client/back/methods/client/filter.js b/modules/client/back/methods/client/filter.js
index 3e1ea43bb..1ae569fd3 100644
--- a/modules/client/back/methods/client/filter.js
+++ b/modules/client/back/methods/client/filter.js
@@ -91,7 +91,18 @@ module.exports = Self => {
case 'search':
return /^\d+$/.test(value)
? {'c.id': {inq: value}}
- : {'c.name': {like: `%${value}%`}};
+ : {or: [
+ {'c.name': {like: `%${value}%`}},
+ {'c.socialName': {like: `%${value}%`}},
+ ]};
+ case 'phone':
+ return {or: [
+ {'c.phone': {like: `%${value}%`}},
+ {'c.mobile': {like: `%${value}%`}},
+ ]};
+ case 'zoneFk':
+ param = 'a.postalCode';
+ return {[param]: {inq: postalCode}};
case 'name':
case 'salesPersonFk':
case 'fi':
@@ -100,12 +111,8 @@ module.exports = Self => {
case 'postcode':
case 'provinceFk':
case 'email':
- case 'phone':
param = `c.${param}`;
- return {[param]: value};
- case 'zoneFk':
- param = 'a.postalCode';
- return {[param]: {inq: postalCode}};
+ return {[param]: {like: `%${value}%`}};
}
});
@@ -119,6 +126,7 @@ module.exports = Self => {
c.fi,
c.socialName,
c.phone,
+ c.mobile,
c.city,
c.postcode,
c.email,
@@ -132,7 +140,7 @@ module.exports = Self => {
LEFT JOIN account.user u ON u.id = c.salesPersonFk
LEFT JOIN province p ON p.id = c.provinceFk
JOIN vn.address a ON a.clientFk = c.id
- `
+ `
);
stmt.merge(conn.makeWhere(filter.where));
diff --git a/modules/route/back/methods/route/getTickets.js b/modules/route/back/methods/route/getTickets.js
index 18e5abaf2..708644c1a 100644
--- a/modules/route/back/methods/route/getTickets.js
+++ b/modules/route/back/methods/route/getTickets.js
@@ -33,36 +33,36 @@ module.exports = Self => {
const stmt = new ParameterizedSQL(
`SELECT
- t.id,
- t.packages,
- t.warehouseFk,
- t.nickname,
- t.clientFk,
- t.priority,
- t.addressFk,
- st.code AS ticketStateCode,
- st.name AS ticketStateName,
- wh.name AS warehouseName,
- tob.description AS ticketObservation,
- a.street,
- a.postalCode,
- a.city,
- am.name AS agencyModeName,
- u.nickname AS userNickname,
- vn.ticketTotalVolume(t.id) AS volume,
- tob.description
- FROM route r
- JOIN ticket t ON t.routeFk = r.id
- LEFT JOIN ticketState ts ON ts.ticketFk = t.id
- LEFT JOIN state st ON st.id = ts.stateFk
- LEFT JOIN warehouse wh ON wh.id = t.warehouseFk
- LEFT JOIN ticketObservation tob ON tob.ticketFk = t.id
- LEFT JOIN observationType ot ON tob.observationTypeFk = ot.id
- AND ot.code = 'delivery'
- LEFT JOIN address a ON a.id = t.addressFk
- LEFT JOIN agencyMode am ON am.id = t.agencyModeFk
- LEFT JOIN account.user u ON u.id = r.workerFk
- LEFT JOIN vehicle v ON v.id = r.vehicleFk`
+ t.id,
+ t.packages,
+ t.warehouseFk,
+ t.nickname,
+ t.clientFk,
+ t.priority,
+ t.addressFk,
+ st.code AS ticketStateCode,
+ st.name AS ticketStateName,
+ wh.name AS warehouseName,
+ tob.description AS ticketObservation,
+ a.street,
+ a.postalCode,
+ a.city,
+ am.name AS agencyModeName,
+ u.nickname AS userNickname,
+ vn.ticketTotalVolume(t.id) AS volume,
+ tob.description
+ FROM vn.route r
+ JOIN ticket t ON t.routeFk = r.id
+ LEFT JOIN ticketState ts ON ts.ticketFk = t.id
+ LEFT JOIN state st ON st.id = ts.stateFk
+ LEFT JOIN warehouse wh ON wh.id = t.warehouseFk
+ LEFT JOIN observationType ot ON ot.code = 'delivery'
+ LEFT JOIN ticketObservation tob ON tob.ticketFk = t.id
+ AND tob.observationTypeFk = ot.id
+ LEFT JOIN address a ON a.id = t.addressFk
+ LEFT JOIN agencyMode am ON am.id = t.agencyModeFk
+ LEFT JOIN account.user u ON u.id = r.workerFk
+ LEFT JOIN vehicle v ON v.id = r.vehicleFk`
);
if (!filter.where) filter.where = {};
diff --git a/modules/worker/back/methods/worker-time-control-mail/checkInbox.js b/modules/worker/back/methods/worker-time-control-mail/checkInbox.js
new file mode 100644
index 000000000..7825f38b8
--- /dev/null
+++ b/modules/worker/back/methods/worker-time-control-mail/checkInbox.js
@@ -0,0 +1,181 @@
+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',
+ reason: 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
+ });
+ }
+};
diff --git a/modules/worker/back/methods/worker-time-control/sendMail.js b/modules/worker/back/methods/worker-time-control/sendMail.js
index cd9cacbd7..b38405c1d 100644
--- a/modules/worker/back/methods/worker-time-control/sendMail.js
+++ b/modules/worker/back/methods/worker-time-control/sendMail.js
@@ -332,18 +332,9 @@ module.exports = Self => {
}, 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);
+ const url = `${salix.url}worker/${previousWorkerFk}/time-control?timestamp=${timestamp}`;
- query = `INSERT IGNORE INTO workerTimeControlMail (workerFk, year, week)
- VALUES (?, ?, ?);`;
- await Self.rawSql(query, [previousWorkerFk, args.year, args.week], myOptions);
+ await models.WorkerTimeControl.weeklyHourRecordEmail(ctx, previousReceiver, args.week, args.year, url);
previousWorkerFk = day.workerFk;
previousReceiver = day.receiver;
diff --git a/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js b/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js
index 4cc6e54e3..24bfd6904 100644
--- a/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js
+++ b/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js
@@ -2,15 +2,12 @@ const models = require('vn-loopback/server/server').models;
describe('workerTimeControl sendMail()', () => {
const workerId = 18;
- const ctx = {
- req: {
- __: value => {
- return value;
- }
- },
- args: {}
-
+ const activeCtx = {
+ getLocale: () => {
+ return 'en';
+ }
};
+ const ctx = {req: activeCtx, args: {}};
it('should fill time control of a worker without records in Journey and with rest', async() => {
const tx = await models.WorkerTimeControl.beginTransaction({});
diff --git a/modules/worker/back/methods/worker-time-control/weeklyHourRecordEmail.js b/modules/worker/back/methods/worker-time-control/weeklyHourRecordEmail.js
new file mode 100644
index 000000000..0cf614e57
--- /dev/null
+++ b/modules/worker/back/methods/worker-time-control/weeklyHourRecordEmail.js
@@ -0,0 +1,53 @@
+const {Email} = require('vn-print');
+
+module.exports = Self => {
+ Self.remoteMethodCtx('weeklyHourRecordEmail', {
+ description: 'Sends the buyer waste email',
+ accessType: 'WRITE',
+ accepts: [
+ {
+ arg: 'recipient',
+ type: 'string',
+ description: 'The recipient email',
+ required: true,
+ },
+ {
+ arg: 'week',
+ type: 'number',
+ required: true,
+ },
+ {
+ arg: 'year',
+ type: 'number',
+ required: true
+ },
+ {
+ arg: 'url',
+ type: 'string',
+ required: true
+ }
+ ],
+ returns: {
+ type: ['object'],
+ root: true
+ },
+ http: {
+ path: '/weekly-hour-hecord-email',
+ verb: 'POST'
+ }
+ });
+
+ Self.weeklyHourRecordEmail = async(ctx, recipient, week, year, url) => {
+ const params = {
+ recipient: recipient,
+ lang: ctx.req.getLocale(),
+ week: week,
+ year: year,
+ url: url
+ };
+
+ const email = new Email('weekly-hour-record', params);
+
+ return email.send();
+ };
+};
diff --git a/modules/worker/back/models/worker-time-control-mail.js b/modules/worker/back/models/worker-time-control-mail.js
new file mode 100644
index 000000000..36f3851b6
--- /dev/null
+++ b/modules/worker/back/models/worker-time-control-mail.js
@@ -0,0 +1,3 @@
+module.exports = Self => {
+ require('../methods/worker-time-control-mail/checkInbox')(Self);
+};
diff --git a/modules/worker/back/models/worker-time-control.js b/modules/worker/back/models/worker-time-control.js
index 9f802511a..7339f5d15 100644
--- a/modules/worker/back/models/worker-time-control.js
+++ b/modules/worker/back/models/worker-time-control.js
@@ -7,6 +7,7 @@ module.exports = Self => {
require('../methods/worker-time-control/updateTimeEntry')(Self);
require('../methods/worker-time-control/sendMail')(Self);
require('../methods/worker-time-control/updateWorkerTimeControlMail')(Self);
+ require('../methods/worker-time-control/weeklyHourRecordEmail')(Self);
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')
diff --git a/print/common/css/misc.css b/print/common/css/misc.css
index df8bf571a..ce6c641a0 100644
--- a/print/common/css/misc.css
+++ b/print/common/css/misc.css
@@ -49,4 +49,10 @@
.page-break-after {
page-break-after: always;
+}
+
+.ellipsize {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
}
\ No newline at end of file
diff --git a/print/templates/email/weekly-hour-record/assets/css/import.js b/print/templates/email/weekly-hour-record/assets/css/import.js
new file mode 100644
index 000000000..1582b82c5
--- /dev/null
+++ b/print/templates/email/weekly-hour-record/assets/css/import.js
@@ -0,0 +1,12 @@
+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();
+
diff --git a/print/templates/email/weekly-hour-record/locale/en.yml b/print/templates/email/weekly-hour-record/locale/en.yml
new file mode 100644
index 000000000..817e5451e
--- /dev/null
+++ b/print/templates/email/weekly-hour-record/locale/en.yml
@@ -0,0 +1,6 @@
+subject: Weekly time log
+title: Record of hours week {0} year {1}
+dear: Dear worker
+description: Access the following link: {{$t('dear')}},
+ {0}
+ Click 'SATISFIED' if you agree with the hours worked. Otherwise, press 'NOT SATISFIED', detailing the cause of the disagreement.
diff --git a/print/templates/email/weekly-hour-record/locale/es.yml b/print/templates/email/weekly-hour-record/locale/es.yml
new file mode 100644
index 000000000..b70862f16
--- /dev/null
+++ b/print/templates/email/weekly-hour-record/locale/es.yml
@@ -0,0 +1,6 @@
+subject: Registro de horas semanal
+title: Registro de horas semana {0} año {1}
+dear: Estimado trabajador
+description: Acceda al siguiente enlace:
+ {0}
+ Pulse 'CONFORME' si esta de acuerdo con las horas trabajadas. En caso contrario pulse 'NO CONFORME', detallando la causa de la disconformidad.
diff --git a/print/templates/email/weekly-hour-record/weekly-hour-record.html b/print/templates/email/weekly-hour-record/weekly-hour-record.html
new file mode 100644
index 000000000..84abb4c61
--- /dev/null
+++ b/print/templates/email/weekly-hour-record/weekly-hour-record.html
@@ -0,0 +1,9 @@
+{{ $t('title', [week, year]) }}
+
-
-
-
-
-
-
-
- {{labelData.levelV}}
- {{labelData.ticketFk}} ⬸ {{labelData.clientFk}}
- {{labelData.shipped}}
-
-
-
- {{labelData.workerCode}}
-
-
- {{labelData.labelCount}}
-
-
- {{labelData.size}}
-
-
-
- {{labelData.lineCount}}
-
-
-
- {{labelData.nickName}}
- {{labelData.agencyHour}}
-
+ {{labelData.collectionFk ? `${labelData.collectionFk} ~ ${labelData.wagon}-${labelData.level}` : '-'.repeat(23)}} + | ++ {{labelData.clientFk ? `${labelData.ticketFk} « ${labelData.clientFk}` : labelData.ticketFk}} + | +{{labelData.shipped ? labelData.shipped : '---'}} | +|
+ | {{labelData.workerCode ? labelData.workerCode : '---'}} | +||
{{labelData.labelCount ? labelData.labelCount : 0}} | +|||
{{labelData.code == 'plant' ? labelData.size + 'cm' : labelData.volume + 'm³'}} | +|||
{{labelData.agencyDescription}} |
+ {{labelData.lineCount ? labelData.lineCount : 0}} | +||
{{labelData.nickName ? labelData.nickName : '---'}} | +{{labelData.shipped ? labelData.shippedHour : labelData.zoneHour}} | +