From 786ba0d614995377d769e54a4f9897a60e6a10a2 Mon Sep 17 00:00:00 2001 From: vicent Date: Mon, 31 Oct 2022 07:34:34 +0100 Subject: [PATCH 01/21] feat: inserta fichadas de los teletrabajadores --- db/dump/fixtures.sql | 30 +- .../methods/worker-time-control/sendMail.js | 336 ++++++++++++++++++ modules/worker/back/model-config.json | 6 + modules/worker/back/models/journey.json | 27 ++ modules/worker/back/models/time.json | 21 ++ .../worker/back/models/worker-time-control.js | 1 + .../back/models/worker-time-control.json | 3 + modules/worker/front/time-control/index.html | 12 + .../worker/front/time-control/locale/es.yml | 4 +- 9 files changed, 434 insertions(+), 6 deletions(-) create mode 100644 modules/worker/back/methods/worker-time-control/sendMail.js create mode 100644 modules/worker/back/models/journey.json create mode 100644 modules/worker/back/models/time.json diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 5b769e2851..3d048f2cc8 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -2266,12 +2266,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 @@ -2677,3 +2681,19 @@ INSERT INTO `vn`.`ticketCollection` (`ticketFk`, `collectionFk`, `created`, `lev UPDATE `account`.`user` SET `hasGrant` = 1 WHERE `id` = 66; + +INSERT INTO `postgresql`.`journey` (`journey_id`, `day_id`, `start`, `end`, `business_id`) +VALUES + (1, 1, '09:00:00', '13:00:00', 18), + (2, 1, '14:00:00', '19:00:00', 18); + -- (3, 3, '12:30:00', '19:00:00', 18), + -- (4, 4, '12:30:00', '19:00:00', 18), + -- (5, 5, '12:30:00', '19:00:00', 18), + -- (6, 6, '12:30:00', '19:00:00', 18), + -- (7, 1, '07:00:00', '12:00:00', 19), + -- (8, 2, '07:00:00', '12:00:00', 19), + -- (9, 3, '07:00:00', '12:00:00', 19), + -- (10, 4, '07:00:00', '12:00:00', 19), + -- (11, 5, '07:00:00', '12:00:00', 19), + -- (12, 6, '07:00:00', '12:00:00', 19); + diff --git a/modules/worker/back/methods/worker-time-control/sendMail.js b/modules/worker/back/methods/worker-time-control/sendMail.js new file mode 100644 index 0000000000..9701859512 --- /dev/null +++ b/modules/worker/back/methods/worker-time-control/sendMail.js @@ -0,0 +1,336 @@ +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', + required: true + }, + { + arg: 'year', + type: 'number', + required: true + }], + returns: [{ + type: 'Object', + root: true + }], + http: { + path: `/sendMail`, + verb: 'POST' + } + }); + + Self.sendMail = async(ctx, options) => { + const models = Self.app.models; + const args = ctx.args; + const myOptions = {}; + const conn = Self.dataSource.connector; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + const stmts = []; + let stmt; + + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate'); + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeBusinessCalculate'); + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate1'); + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeBusinessCalculate1'); + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.error'); + + const today = new Date(); + + const started = new Date(today.setDate(today.getDate() - today.getDay())); + started.setHours(0, 0, 0, 0); + + const ended = new Date(today.setDate(today.getDate() - today.getDay() + 6)); + ended.setHours(23, 59, 59, 999); + + 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_calculateAll(?, ?)`, [args.workerId, started, ended]); + stmts.push(stmt); + + stmt = new ParameterizedSQL(`CALL vn.timeBusiness_calculateAll(?, ?)`, [args.workerId, started, ended]); + stmts.push(stmt); + } else { // args.workerId IS NULL + 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); + } + + stmts.push(`CREATE TEMPORARY TABLE tmp.timeControlCalculate1 + SELECT * FROM tmp.timeControlCalculate`); + + stmts.push(`CREATE TEMPORARY TABLE tmp.timeBusinessCalculate1 + SELECT * FROM tmp.timeBusinessCalculate`); + + const index = stmts.push(`SELECT CONCAT(u.name, '@verdnatura.es'), -- vReceiver + u.id workerFk, -- vWorkerFk + tb.dated, -- vDated + tb.timeWorkDecimal, -- vTimeWorkDecimal + tb.timeWorkSexagesimal timeWorkSexagesimal, -- vTimeWorkSexagesimal + tb.timeTable, -- vTimeTable + tc.timeWorkDecimal timeWorkedDecimal, -- vTimeWorkedDecimal + tc.timeWorkSexagesimal timeWorkedSexagesimal, -- vTimeWorkedSexagesimal + tb.type, -- vAbsenceType + tb.businessFk, -- vBusinessFk + tb.permissionRate, -- vPermissionRate + d.isTeleworking -- vIsTeleworking + 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 + LEFT JOIN user u2 ON u2.id = w.bossFk + 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.timeBusinessCalculate1 tb + LEFT JOIN tmp.timeControlCalculate1 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(vWorkerFk, u.id) = u.id + AND b.companyCodeFk = 'VNL' + AND w.businessFk + ORDER BY u.id, tb.dated`) - 1; + + const sql = ParameterizedSQL.join(stmts, ';'); + const days = await conn.executeStmt(sql, myOptions); + + for (let day of days[index]) { + const weekDay = day.dated.getDay() + 1; + const journeys = await models.Journey.find({ + where: { + business_id: day.businessFk, + day_id: weekDay + } + }, myOptions); + console.log('journeys: ', journeys); + + for (let journey of journeys) { + // const minStart = journeys.reduce(function(prev, curr) { + // return curr.start < prev.start ? curr : prev; + // }); + // if (journey == minStart) { + // const [startHours, startMinutes, startSeconds] = journey.start.split(':'); + // await models.WorkerTimeControl.create({ + // userFk: day.workerFk, + // timed: day.dated.setHours(startHours, startMinutes, startSeconds), + // manual: true, + // isSendMail: true + // }, myOptions); + + // const [hoursWork, minutesWork, secondsWork] = day.timeWorkSexagesimal.split(':'); + // await models.WorkerTimeControl.create({ + // userFk: day.workerFk, + // timed: day.dated.setHours( + // parseInt(startHours) + parseInt(hoursWork), + // parseInt(startMinutes) + parseInt(minutesWork), + // parseInt(startSeconds) + parseInt(secondsWork) + // ), + // manual: true, + // isSendMail: true + // }, myOptions); + // } + } + + if (day.timeWorkDecimal > 0 && day.timeWorkedDecimal == null && (day.permissionRate ? day.permissionRate : true)) { + if (day.timeTable == null) { + await models.WorkerTimeControl.create({ + userFk: day.workerFk, + timed: day.dated.setHours(8), + manual: true, + direction: 'in', + isSendMail: true + }, myOptions); + + if (day.timeWorkDecimal >= 5) { + await models.WorkerTimeControl.create({ + userFk: day.workerFk, + timed: day.dated.setHours(9), + manual: true, + direction: 'middle', + isSendMail: true + }, myOptions); + + await models.WorkerTimeControl.create({ + userFk: day.workerFk, + timed: day.dated.setHours(9, 20), + manual: true, + direction: 'middle', + isSendMail: true + }, myOptions); + } + + const [hoursWork, minutesWork, secondsWork] = day.timeWorkSexagesimal.split(':'); + await models.WorkerTimeControl.create({ + userFk: day.workerFk, + timed: day.dated.setHours(8 + parseInt(hoursWork), minutesWork, secondsWork), + manual: true, + direction: 'out', + isSendMail: true + }, myOptions); + } else { + const weekDay = day.dated.getDay() + 1; + const journeys = await models.Journey.find({ + where: { + business_id: day.businessFk, + day_id: weekDay + } + }, myOptions); + console.log('journeys: ', journeys); + + let timeTableDecimalInSeconds = 0; + + for (let journey of journeys) { + const start = new Date(); + const [startHours, startMinutes, startSeconds] = journey.start.split(':'); + start.setHours(startHours, startMinutes, startSeconds); + + const end = new Date(); + const [endHours, endMinutes, endSeconds] = journey.end.split(':'); + end.setHours(endHours, endMinutes, endSeconds); + + const result = (end - start) / 1000; + timeTableDecimalInSeconds += result; + } + + for (let journey of journeys) { + const timeTableDecimal = timeTableDecimalInSeconds / 3600; + if (day.timeWorkDecimal == timeTableDecimal) { + const [startHours, startMinutes, startSeconds] = journey.start.split(':'); + await models.WorkerTimeControl.create({ + userFk: day.workerFk, + timed: day.dated.setHours(startHours, startMinutes, startSeconds), + manual: true, + isSendMail: true + }, myOptions); + + const [endHours, endMinutes, endSeconds] = journey.end.split(':'); + await models.WorkerTimeControl.create({ + userFk: day.workerFk, + timed: day.dated.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 [startHours, startMinutes, startSeconds] = journey.start.split(':'); + await models.WorkerTimeControl.create({ + userFk: day.workerFk, + timed: day.dated.setHours(startHours, startMinutes, startSeconds), + manual: true, + isSendMail: true + }, myOptions); + + const [hoursWork, minutesWork, secondsWork] = day.timeWorkSexagesimal.split(':'); + await models.WorkerTimeControl.create({ + userFk: day.workerFk, + timed: day.dated.setHours( + parseInt(startHours) + parseInt(hoursWork), + parseInt(startMinutes) + parseInt(minutesWork), + parseInt(startSeconds) + parseInt(secondsWork) + ), + manual: true, + isSendMail: true + }, myOptions); + } + } + + if (day.timeWorkDecimal >= 5) { + const minStart = journeys.reduce(function(prev, curr) { + return curr.start < prev.start ? curr : prev; + }); + if (journey == minStart) { + const [startHours, startMinutes, startSeconds] = journey.start.split(':'); + await models.WorkerTimeControl.create({ + userFk: day.workerFk, + timed: day.dated.setHours(parseInt(startHours) + 1, startMinutes, startSeconds), + manual: true, + isSendMail: true + }, myOptions); + + await models.WorkerTimeControl.create({ + userFk: day.workerFk, + timed: day.dated.setHours(parseInt(startHours) + 1, parseInt(startMinutes) + 20, startSeconds), + manual: true, + isSendMail: true + }, myOptions); + } + } + } + + const firstWorkerTimeControl = await models.WorkerTimeControl.findOne({ + where: { + userFk: day.workerFk, + timed: {between: [day.dated, day.dated.setHours(23, 59, 59, 999)]} + }, + order: 'timed ASC', + limit: 1 + }, myOptions); + + if (firstWorkerTimeControl) firstWorkerTimeControl.updateAttribute('direction', 'in', myOptions); + + const lastWorkerTimeControl = await models.WorkerTimeControl.findOne({ + where: { + userFk: day.workerFk, + timed: {between: [day.dated, day.dated.setHours(23, 59, 59, 999)]} + }, + order: 'timed DESC', + limit: 1 + }, myOptions); + + if (lastWorkerTimeControl) lastWorkerTimeControl.updateAttribute('direction', 'out', myOptions); + } + } + } + return true; + }; +}; diff --git a/modules/worker/back/model-config.json b/modules/worker/back/model-config.json index c155e331de..bca24663bd 100644 --- a/modules/worker/back/model-config.json +++ b/modules/worker/back/model-config.json @@ -20,6 +20,12 @@ "EducationLevel": { "dataSource": "vn" }, + "Journey": { + "dataSource": "vn" + }, + "Time": { + "dataSource": "vn" + }, "WorkCenter": { "dataSource": "vn" }, diff --git a/modules/worker/back/models/journey.json b/modules/worker/back/models/journey.json new file mode 100644 index 0000000000..b7d5f28176 --- /dev/null +++ b/modules/worker/back/models/journey.json @@ -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" + } + } +} diff --git a/modules/worker/back/models/time.json b/modules/worker/back/models/time.json new file mode 100644 index 0000000000..df92575405 --- /dev/null +++ b/modules/worker/back/models/time.json @@ -0,0 +1,21 @@ +{ + "name": "Time", + "base": "VnModel", + "options": { + "mysql": { + "table": "time" + } + }, + "properties": { + "dated": { + "id": true, + "type": "date" + }, + "year": { + "type": "number" + }, + "week": { + "type": "number" + } + } +} diff --git a/modules/worker/back/models/worker-time-control.js b/modules/worker/back/models/worker-time-control.js index 45f4e2194e..928606b7d5 100644 --- a/modules/worker/back/models/worker-time-control.js +++ b/modules/worker/back/models/worker-time-control.js @@ -5,6 +5,7 @@ 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); Self.rewriteDbError(function(err) { if (err.code === 'ER_DUP_ENTRY') diff --git a/modules/worker/back/models/worker-time-control.json b/modules/worker/back/models/worker-time-control.json index ab07802ca1..bc3e535016 100644 --- a/modules/worker/back/models/worker-time-control.json +++ b/modules/worker/back/models/worker-time-control.json @@ -22,6 +22,9 @@ }, "direction": { "type": "string" + }, + "isSendMail": { + "type": "boolean" } }, "relations": { diff --git a/modules/worker/front/time-control/index.html b/modules/worker/front/time-control/index.html index 170d88b218..1b60dcce08 100644 --- a/modules/worker/front/time-control/index.html +++ b/modules/worker/front/time-control/index.html @@ -77,6 +77,18 @@ + + + + + + + +
diff --git a/modules/worker/front/time-control/locale/es.yml b/modules/worker/front/time-control/locale/es.yml index 8b2486f055..cace38291f 100644 --- a/modules/worker/front/time-control/locale/es.yml +++ b/modules/worker/front/time-control/locale/es.yml @@ -10,4 +10,6 @@ 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 \ No newline at end of file +The entry type can't be empty: El tipo de fichada no puede quedar vacía +Satisfied: Conforme +Not satisfied: No conforme \ No newline at end of file From 838402f05ab13c42bd2bf2a9789a8c2279f4025d Mon Sep 17 00:00:00 2001 From: vicent Date: Mon, 31 Oct 2022 09:55:05 +0100 Subject: [PATCH 02/21] add fixtures --- db/dump/fixtures.sql | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 3d048f2cc8..2bac276c1f 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -2685,15 +2685,14 @@ UPDATE `account`.`user` INSERT INTO `postgresql`.`journey` (`journey_id`, `day_id`, `start`, `end`, `business_id`) VALUES (1, 1, '09:00:00', '13:00:00', 18), - (2, 1, '14:00:00', '19:00:00', 18); - -- (3, 3, '12:30:00', '19:00:00', 18), - -- (4, 4, '12:30:00', '19:00:00', 18), - -- (5, 5, '12:30:00', '19:00:00', 18), - -- (6, 6, '12:30:00', '19:00:00', 18), - -- (7, 1, '07:00:00', '12:00:00', 19), - -- (8, 2, '07:00:00', '12:00:00', 19), - -- (9, 3, '07:00:00', '12:00:00', 19), - -- (10, 4, '07:00:00', '12:00:00', 19), - -- (11, 5, '07:00:00', '12:00:00', 19), - -- (12, 6, '07:00:00', '12:00:00', 19); + (2, 1, '14:00:00', '19:00:00', 18), + (3, 3, '12:30:00', '19:00:00', 18), + (4, 4, '01:30:00', '07:30:00', 18), + (5, 5, '01:00:00', '09:00:00', 18), + (6, 6, '02:00:00', '08:00:00', 18), + (7, 1, '07:00:00', '12:00:00', 19), + (8, 2, '09:00:00', '17:00:00', 19), + (9, 3, '15:00:00', '19:00:00', 19), + (10, 4, '07:00:00', '14:00:00', 19), + (11, 5, '08:00:00', '13:00:00', 19); From b17fe0d18c38c96be4646a9139ce6269aff181a4 Mon Sep 17 00:00:00 2001 From: vicent Date: Mon, 31 Oct 2022 09:55:11 +0100 Subject: [PATCH 03/21] refator code --- .../methods/worker-time-control/sendMail.js | 125 +++++++----------- 1 file changed, 50 insertions(+), 75 deletions(-) diff --git a/modules/worker/back/methods/worker-time-control/sendMail.js b/modules/worker/back/methods/worker-time-control/sendMail.js index 9701859512..e1b2c86e7a 100644 --- a/modules/worker/back/methods/worker-time-control/sendMail.js +++ b/modules/worker/back/methods/worker-time-control/sendMail.js @@ -42,20 +42,30 @@ module.exports = Self => { const stmts = []; let stmt; + 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; + } + + 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'); stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate1'); stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeBusinessCalculate1'); stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.error'); - const today = new Date(); - - const started = new Date(today.setDate(today.getDate() - today.getDay())); - started.setHours(0, 0, 0, 0); - - const ended = new Date(today.setDate(today.getDate() - today.getDay() + 6)); - ended.setHours(23, 59, 59, 999); - if (args.workerId) { await models.WorkerTimeControl.destroyAll({ userFk: args.workerId, @@ -77,7 +87,7 @@ module.exports = Self => { stmt = new ParameterizedSQL(`CALL vn.timeBusiness_calculateAll(?, ?)`, [args.workerId, started, ended]); stmts.push(stmt); - } else { // args.workerId IS NULL + } else { await models.WorkerTimeControl.destroyAll({ timed: {between: [started, ended]}, isSendMail: true @@ -104,18 +114,19 @@ module.exports = Self => { stmts.push(`CREATE TEMPORARY TABLE tmp.timeBusinessCalculate1 SELECT * FROM tmp.timeBusinessCalculate`); - const index = stmts.push(`SELECT CONCAT(u.name, '@verdnatura.es'), -- vReceiver - u.id workerFk, -- vWorkerFk - tb.dated, -- vDated - tb.timeWorkDecimal, -- vTimeWorkDecimal - tb.timeWorkSexagesimal timeWorkSexagesimal, -- vTimeWorkSexagesimal - tb.timeTable, -- vTimeTable - tc.timeWorkDecimal timeWorkedDecimal, -- vTimeWorkedDecimal - tc.timeWorkSexagesimal timeWorkedSexagesimal, -- vTimeWorkedSexagesimal - tb.type, -- vAbsenceType - tb.businessFk, -- vBusinessFk - tb.permissionRate, -- vPermissionRate - d.isTeleworking -- vIsTeleworking + const index = stmts.push(` + SELECT CONCAT(u.name, '@verdnatura.es'), + 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 @@ -128,7 +139,8 @@ module.exports = Self => { IF(tc.timeWorkDecimal > 0, FALSE, IF(tb.timeWorkDecimal > 0, TRUE, FALSE)), TRUE))isTeleworkingWeek FROM tmp.timeBusinessCalculate1 tb - LEFT JOIN tmp.timeControlCalculate1 tc ON tc.userFk = tb.userFk AND tc.dated = tb.dated + LEFT JOIN tmp.timeControlCalculate1 tc ON tc.userFk = tb.userFk + AND tc.dated = tb.dated GROUP BY tb.userFk HAVING isTeleworkingWeek > 0 )sub ON sub.userFk = u.id @@ -142,47 +154,12 @@ module.exports = Self => { const days = await conn.executeStmt(sql, myOptions); for (let day of days[index]) { - const weekDay = day.dated.getDay() + 1; - const journeys = await models.Journey.find({ - where: { - business_id: day.businessFk, - day_id: weekDay - } - }, myOptions); - console.log('journeys: ', journeys); - - for (let journey of journeys) { - // const minStart = journeys.reduce(function(prev, curr) { - // return curr.start < prev.start ? curr : prev; - // }); - // if (journey == minStart) { - // const [startHours, startMinutes, startSeconds] = journey.start.split(':'); - // await models.WorkerTimeControl.create({ - // userFk: day.workerFk, - // timed: day.dated.setHours(startHours, startMinutes, startSeconds), - // manual: true, - // isSendMail: true - // }, myOptions); - - // const [hoursWork, minutesWork, secondsWork] = day.timeWorkSexagesimal.split(':'); - // await models.WorkerTimeControl.create({ - // userFk: day.workerFk, - // timed: day.dated.setHours( - // parseInt(startHours) + parseInt(hoursWork), - // parseInt(startMinutes) + parseInt(minutesWork), - // parseInt(startSeconds) + parseInt(secondsWork) - // ), - // manual: true, - // isSendMail: true - // }, myOptions); - // } - } - - if (day.timeWorkDecimal > 0 && day.timeWorkedDecimal == null && (day.permissionRate ? day.permissionRate : true)) { + if (day.timeWorkDecimal > 0 && day.timeWorkedDecimal == null + && day.permissionRate ? day.permissionRate : true) { if (day.timeTable == null) { await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: day.dated.setHours(8), + timed: new Date(day.dated.setHours(8)), manual: true, direction: 'in', isSendMail: true @@ -191,7 +168,7 @@ module.exports = Self => { if (day.timeWorkDecimal >= 5) { await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: day.dated.setHours(9), + timed: new Date(day.dated.setHours(9)), manual: true, direction: 'middle', isSendMail: true @@ -199,7 +176,7 @@ module.exports = Self => { await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: day.dated.setHours(9, 20), + timed: new Date(day.dated.setHours(9, 20)), manual: true, direction: 'middle', isSendMail: true @@ -209,23 +186,21 @@ module.exports = Self => { const [hoursWork, minutesWork, secondsWork] = day.timeWorkSexagesimal.split(':'); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: day.dated.setHours(8 + parseInt(hoursWork), minutesWork, secondsWork), + timed: new Date(day.dated.setHours(8 + parseInt(hoursWork), minutesWork, secondsWork)), manual: true, direction: 'out', isSendMail: true }, myOptions); } else { - const weekDay = day.dated.getDay() + 1; + const weekDay = day.dated.getDay(); const journeys = await models.Journey.find({ where: { business_id: day.businessFk, day_id: weekDay } }, myOptions); - console.log('journeys: ', journeys); let timeTableDecimalInSeconds = 0; - for (let journey of journeys) { const start = new Date(); const [startHours, startMinutes, startSeconds] = journey.start.split(':'); @@ -245,7 +220,7 @@ module.exports = Self => { const [startHours, startMinutes, startSeconds] = journey.start.split(':'); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: day.dated.setHours(startHours, startMinutes, startSeconds), + timed: new Date(day.dated.setHours(startHours, startMinutes, startSeconds)), manual: true, isSendMail: true }, myOptions); @@ -253,7 +228,7 @@ module.exports = Self => { const [endHours, endMinutes, endSeconds] = journey.end.split(':'); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: day.dated.setHours(endHours, endMinutes, endSeconds), + timed: new Date(day.dated.setHours(endHours, endMinutes, endSeconds)), manual: true, isSendMail: true }, myOptions); @@ -265,7 +240,7 @@ module.exports = Self => { const [startHours, startMinutes, startSeconds] = journey.start.split(':'); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: day.dated.setHours(startHours, startMinutes, startSeconds), + timed: new Date(day.dated.setHours(startHours, startMinutes, startSeconds)), manual: true, isSendMail: true }, myOptions); @@ -273,11 +248,11 @@ module.exports = Self => { const [hoursWork, minutesWork, secondsWork] = day.timeWorkSexagesimal.split(':'); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: day.dated.setHours( + timed: new Date(day.dated.setHours( parseInt(startHours) + parseInt(hoursWork), parseInt(startMinutes) + parseInt(minutesWork), parseInt(startSeconds) + parseInt(secondsWork) - ), + )), manual: true, isSendMail: true }, myOptions); @@ -292,14 +267,14 @@ module.exports = Self => { const [startHours, startMinutes, startSeconds] = journey.start.split(':'); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: day.dated.setHours(parseInt(startHours) + 1, startMinutes, startSeconds), + timed: new Date(day.dated.setHours(parseInt(startHours) + 1, startMinutes, startSeconds)), manual: true, isSendMail: true }, myOptions); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: day.dated.setHours(parseInt(startHours) + 1, parseInt(startMinutes) + 20, startSeconds), + timed: new Date(day.dated.setHours(parseInt(startHours) + 1, parseInt(startMinutes) + 20, startSeconds)), manual: true, isSendMail: true }, myOptions); @@ -310,7 +285,7 @@ module.exports = Self => { const firstWorkerTimeControl = await models.WorkerTimeControl.findOne({ where: { userFk: day.workerFk, - timed: {between: [day.dated, day.dated.setHours(23, 59, 59, 999)]} + timed: {between: [new Date(day.dated.setHours(0, 0, 0, 0)), new Date(day.dated.setHours(23, 59, 59, 999))]} }, order: 'timed ASC', limit: 1 @@ -321,7 +296,7 @@ module.exports = Self => { const lastWorkerTimeControl = await models.WorkerTimeControl.findOne({ where: { userFk: day.workerFk, - timed: {between: [day.dated, day.dated.setHours(23, 59, 59, 999)]} + timed: {between: [new Date(day.dated.setHours(0, 0, 0, 0)), new Date(day.dated.setHours(23, 59, 59, 999))]} }, order: 'timed DESC', limit: 1 From 273a8cd7c7f3f03c5a4eab15128a60769281334f Mon Sep 17 00:00:00 2001 From: vicent Date: Mon, 31 Oct 2022 13:00:32 +0100 Subject: [PATCH 04/21] refactor code --- .../methods/worker-time-control/sendMail.js | 79 ++++++++++++------- 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/modules/worker/back/methods/worker-time-control/sendMail.js b/modules/worker/back/methods/worker-time-control/sendMail.js index e1b2c86e7a..f13276a263 100644 --- a/modules/worker/back/methods/worker-time-control/sendMail.js +++ b/modules/worker/back/methods/worker-time-control/sendMail.js @@ -12,13 +12,11 @@ module.exports = Self => { }, { arg: 'week', - type: 'number', - required: true + type: 'number' }, { arg: 'year', - type: 'number', - required: true + type: 'number' }], returns: [{ type: 'Object', @@ -53,6 +51,26 @@ module.exports = Self => { return weekStart; } + function getTime(timeString) { + const [hours, minutes, seconds] = timeString.split(':'); + return [parseInt(hours), parseInt(minutes), parseInt(seconds)]; + } + + 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); @@ -155,11 +173,12 @@ module.exports = Self => { for (let day of days[index]) { if (day.timeWorkDecimal > 0 && day.timeWorkedDecimal == null - && day.permissionRate ? day.permissionRate : true) { + && (day.permissionRate ? day.permissionRate : true)) { if (day.timeTable == null) { + const timed = new Date(day.dated); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: new Date(day.dated.setHours(8)), + timed: timed.setHours(8), manual: true, direction: 'in', isSendMail: true @@ -168,7 +187,7 @@ module.exports = Self => { if (day.timeWorkDecimal >= 5) { await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: new Date(day.dated.setHours(9)), + timed: timed.setHours(9), manual: true, direction: 'middle', isSendMail: true @@ -176,17 +195,17 @@ module.exports = Self => { await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: new Date(day.dated.setHours(9, 20)), + timed: timed.setHours(9, 20), manual: true, direction: 'middle', isSendMail: true }, myOptions); } - const [hoursWork, minutesWork, secondsWork] = day.timeWorkSexagesimal.split(':'); + const [hoursWork, minutesWork, secondsWork] = getTime(day.timeWorkSexagesimal); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: new Date(day.dated.setHours(8 + parseInt(hoursWork), minutesWork, secondsWork)), + timed: timed.setHours(8 + hoursWork, minutesWork, secondsWork), manual: true, direction: 'out', isSendMail: true @@ -203,11 +222,11 @@ module.exports = Self => { let timeTableDecimalInSeconds = 0; for (let journey of journeys) { const start = new Date(); - const [startHours, startMinutes, startSeconds] = journey.start.split(':'); + const [startHours, startMinutes, startSeconds] = getTime(journey.start); start.setHours(startHours, startMinutes, startSeconds); const end = new Date(); - const [endHours, endMinutes, endSeconds] = journey.end.split(':'); + const [endHours, endMinutes, endSeconds] = getTime(journey.end); end.setHours(endHours, endMinutes, endSeconds); const result = (end - start) / 1000; @@ -217,18 +236,19 @@ module.exports = Self => { for (let journey of journeys) { const timeTableDecimal = timeTableDecimalInSeconds / 3600; if (day.timeWorkDecimal == timeTableDecimal) { - const [startHours, startMinutes, startSeconds] = journey.start.split(':'); + const timed = new Date(day.dated); + const [startHours, startMinutes, startSeconds] = getTime(journey.start); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: new Date(day.dated.setHours(startHours, startMinutes, startSeconds)), + timed: timed.setHours(startHours, startMinutes, startSeconds), manual: true, isSendMail: true }, myOptions); - const [endHours, endMinutes, endSeconds] = journey.end.split(':'); + const [endHours, endMinutes, endSeconds] = getTime(journey.end); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: new Date(day.dated.setHours(endHours, endMinutes, endSeconds)), + timed: timed.setHours(endHours, endMinutes, endSeconds), manual: true, isSendMail: true }, myOptions); @@ -237,22 +257,21 @@ module.exports = Self => { return curr.start < prev.start ? curr : prev; }); if (journey == minStart) { - const [startHours, startMinutes, startSeconds] = journey.start.split(':'); + const timed = new Date(day.dated); + const [startHours, startMinutes, startSeconds] = getTime(journey.start); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: new Date(day.dated.setHours(startHours, startMinutes, startSeconds)), + timed: timed.setHours(startHours, startMinutes, startSeconds), manual: true, isSendMail: true }, myOptions); - const [hoursWork, minutesWork, secondsWork] = day.timeWorkSexagesimal.split(':'); + const [hoursWork, minutesWork, secondsWork] = getTime(day.timeWorkSexagesimal); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: new Date(day.dated.setHours( - parseInt(startHours) + parseInt(hoursWork), - parseInt(startMinutes) + parseInt(minutesWork), - parseInt(startSeconds) + parseInt(secondsWork) - )), + timed: timed.setHours( + startHours + hoursWork, startMinutes + minutesWork, startSeconds + secondsWork + ), manual: true, isSendMail: true }, myOptions); @@ -264,17 +283,18 @@ module.exports = Self => { return curr.start < prev.start ? curr : prev; }); if (journey == minStart) { - const [startHours, startMinutes, startSeconds] = journey.start.split(':'); + const timed = new Date(day.dated); + const [startHours, startMinutes, startSeconds] = getTime(journey.start); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: new Date(day.dated.setHours(parseInt(startHours) + 1, startMinutes, startSeconds)), + timed: timed.setHours(startHours + 1, startMinutes, startSeconds), manual: true, isSendMail: true }, myOptions); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: new Date(day.dated.setHours(parseInt(startHours) + 1, parseInt(startMinutes) + 20, startSeconds)), + timed: timed.setHours(startHours + 1, startMinutes + 20, startSeconds), manual: true, isSendMail: true }, myOptions); @@ -282,10 +302,11 @@ module.exports = Self => { } } + const timed = new Date(day.dated); const firstWorkerTimeControl = await models.WorkerTimeControl.findOne({ where: { userFk: day.workerFk, - timed: {between: [new Date(day.dated.setHours(0, 0, 0, 0)), new Date(day.dated.setHours(23, 59, 59, 999))]} + timed: {between: [timed.setHours(0, 0, 0, 0), timed.setHours(23, 59, 59, 999)]} }, order: 'timed ASC', limit: 1 @@ -296,7 +317,7 @@ module.exports = Self => { const lastWorkerTimeControl = await models.WorkerTimeControl.findOne({ where: { userFk: day.workerFk, - timed: {between: [new Date(day.dated.setHours(0, 0, 0, 0)), new Date(day.dated.setHours(23, 59, 59, 999))]} + timed: {between: [timed.setHours(0, 0, 0, 0), timed.setHours(23, 59, 59, 999)]} }, order: 'timed DESC', limit: 1 From 1f2ea2c4d8dc8d697da42f87ffa7ad898c090fec Mon Sep 17 00:00:00 2001 From: vicent Date: Mon, 31 Oct 2022 13:30:08 +0100 Subject: [PATCH 05/21] fix: incorrect number of params --- .../methods/worker-time-control/sendMail.js | 80 ++++++++++--------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/modules/worker/back/methods/worker-time-control/sendMail.js b/modules/worker/back/methods/worker-time-control/sendMail.js index f13276a263..fef8a4e2db 100644 --- a/modules/worker/back/methods/worker-time-control/sendMail.js +++ b/modules/worker/back/methods/worker-time-control/sendMail.js @@ -100,10 +100,14 @@ module.exports = Self => { updated: new Date(), state: 'SENDED' }, myOptions); - stmt = new ParameterizedSQL(`CALL vn.timeControl_calculateAll(?, ?)`, [args.workerId, started, ended]); + stmt = new ParameterizedSQL( + `CALL vn.timeControl_calculateByUser(?, ?, ?) + `, [args.workerId, started, ended]); stmts.push(stmt); - stmt = new ParameterizedSQL(`CALL vn.timeBusiness_calculateAll(?, ?)`, [args.workerId, started, ended]); + stmt = new ParameterizedSQL( + `CALL vn.timeBusiness_calculateByUser(?, ?, ?) + `, [args.workerId, started, ended]); stmts.push(stmt); } else { await models.WorkerTimeControl.destroyAll({ @@ -132,41 +136,43 @@ module.exports = Self => { stmts.push(`CREATE TEMPORARY TABLE tmp.timeBusinessCalculate1 SELECT * FROM tmp.timeBusinessCalculate`); - const index = stmts.push(` - SELECT CONCAT(u.name, '@verdnatura.es'), - 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 - LEFT JOIN user u2 ON u2.id = w.bossFk - 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.timeBusinessCalculate1 tb - LEFT JOIN tmp.timeControlCalculate1 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(vWorkerFk, u.id) = u.id - AND b.companyCodeFk = 'VNL' - AND w.businessFk - ORDER BY u.id, tb.dated`) - 1; + stmt = new ParameterizedSQL(` + SELECT CONCAT(u.name, '@verdnatura.es'), + 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 + LEFT JOIN user u2 ON u2.id = w.bossFk + 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.timeBusinessCalculate1 tb + LEFT JOIN tmp.timeControlCalculate1 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); From 3243d35bd7bfc9e15457ff3e6d31edfeed95d84b Mon Sep 17 00:00:00 2001 From: vicent Date: Mon, 31 Oct 2022 14:48:37 +0100 Subject: [PATCH 06/21] feat: send email --- loopback/locale/es.json | 1 + .../methods/worker-time-control/sendMail.js | 22 ++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 67370b3438..69259c8577 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -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 {{author}} ha añadido una ausencia de tipo '{{absenceType}}' a {{employee}} para el día {{dated}}.", "Deleted absence": "El empleado {{author}} ha eliminado una ausencia de tipo '{{absenceType}}' a {{employee}} del día {{dated}}.", "I have deleted the ticket id": "He eliminado el ticket id [{{id}}]({{{url}}})", diff --git a/modules/worker/back/methods/worker-time-control/sendMail.js b/modules/worker/back/methods/worker-time-control/sendMail.js index fef8a4e2db..8716ec172e 100644 --- a/modules/worker/back/methods/worker-time-control/sendMail.js +++ b/modules/worker/back/methods/worker-time-control/sendMail.js @@ -30,6 +30,7 @@ module.exports = Self => { Self.sendMail = async(ctx, options) => { const models = Self.app.models; + const $t = ctx.req.__; // $translate const args = ctx.args; const myOptions = {}; const conn = Self.dataSource.connector; @@ -137,7 +138,7 @@ module.exports = Self => { SELECT * FROM tmp.timeBusinessCalculate`); stmt = new ParameterizedSQL(` - SELECT CONCAT(u.name, '@verdnatura.es'), + SELECT CONCAT(u.name, '@verdnatura.es') receiver, u.id workerFk, tb.dated, tb.timeWorkDecimal, @@ -177,6 +178,9 @@ module.exports = Self => { const sql = ParameterizedSQL.join(stmts, ';'); const days = await conn.executeStmt(sql, myOptions); + let previousWorkerFk = days[index][0].workerFk; + let previousReceiver = days[index][0].receiver; + for (let day of days[index]) { if (day.timeWorkDecimal > 0 && day.timeWorkedDecimal == null && (day.permissionRate ? day.permissionRate : true)) { @@ -332,7 +336,23 @@ module.exports = Self => { if (lastWorkerTimeControl) lastWorkerTimeControl.updateAttribute('direction', 'out', myOptions); } } + + const origin = ctx.req.headers.origin; + const lastDay = days[index][days[index].length - 1]; + if (day.workerFk != previousWorkerFk || day == lastDay) { + await models.Mail.create({ + receiver: previousReceiver, + subject: $t('Record of hours week', { + week: args.week, + year: args.year + }), + body: `${origin}/#!/worker/${previousWorkerFk}/time-control?timestamp=` + }); + previousWorkerFk = day.workerFk; + previousReceiver = day.receiver; + } } + return true; }; }; From 57564325e191850d3a4d556b3e58b87c84049c4a Mon Sep 17 00:00:00 2001 From: vicent Date: Wed, 2 Nov 2022 14:22:18 +0100 Subject: [PATCH 07/21] =?UTF-8?q?feat:=20a=C3=B1adidas=20opciones=20para?= =?UTF-8?q?=20confirmar=20o=20no=20confirmar=20el=20registro=20de=20horas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../methods/worker-time-control/sendMail.js | 460 ++++++++++-------- .../updateWorkerTimeControlMail.js | 61 +++ .../back/models/worker-time-control-mail.json | 3 +- .../worker/back/models/worker-time-control.js | 1 + modules/worker/front/time-control/index.html | 23 +- modules/worker/front/time-control/index.js | 36 ++ .../worker/front/time-control/locale/es.yml | 3 +- 7 files changed, 365 insertions(+), 222 deletions(-) create mode 100644 modules/worker/back/methods/worker-time-control/updateWorkerTimeControlMail.js diff --git a/modules/worker/back/methods/worker-time-control/sendMail.js b/modules/worker/back/methods/worker-time-control/sendMail.js index 8716ec172e..c87df5ceaa 100644 --- a/modules/worker/back/methods/worker-time-control/sendMail.js +++ b/modules/worker/back/methods/worker-time-control/sendMail.js @@ -30,114 +30,105 @@ module.exports = Self => { Self.sendMail = async(ctx, options) => { const models = Self.app.models; - const $t = ctx.req.__; // $translate - const args = ctx.args; - const myOptions = {}; 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; - 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; - } + try { + if (!args.week || !args.year) { + const from = new Date(); + const to = new Date(); - function getTime(timeString) { - const [hours, minutes, seconds] = timeString.split(':'); - return [parseInt(hours), parseInt(minutes), parseInt(seconds)]; - } + const time = await models.Time.findOne({ + where: { + dated: {between: [from.setDate(from.getDate() - 10), to.setDate(to.getDate() - 4)]} + }, + order: 'week ASC' + }, myOptions); - if (!args.week || !args.year) { - const from = new Date(); - const to = new Date(); + args.week = time.week; + args.year = time.year; + } - const time = await models.Time.findOne({ - where: { - dated: {between: [from.setDate(from.getDate() - 10), to.setDate(to.getDate() - 4)]} - }, - order: 'week ASC' - }, myOptions); + const started = getStartDateOfWeekNumber(args.week, args.year); + started.setHours(0, 0, 0, 0); - args.week = time.week; - args.year = time.year; - } + const ended = new Date(started); + ended.setDate(started.getDate() + 6); + ended.setHours(23, 59, 59, 999); - const started = getStartDateOfWeekNumber(args.week, args.year); - started.setHours(0, 0, 0, 0); + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate'); + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeBusinessCalculate'); + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate1'); + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeBusinessCalculate1'); + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.error'); - const ended = new Date(started); - ended.setDate(started.getDate() + 6); - ended.setHours(23, 59, 59, 999); + if (args.workerId) { + await models.WorkerTimeControl.destroyAll({ + userFk: args.workerId, + timed: {between: [started, ended]}, + isSendMail: true + }, myOptions); - stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate'); - stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeBusinessCalculate'); - stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate1'); - stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeBusinessCalculate1'); - stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.error'); + const where = { + workerFk: args.workerId, + year: args.year, + week: args.week + }; + await models.WorkerTimeControlMail.updateAll(where, { + updated: new Date(), state: 'SENDED' + }, myOptions); - 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(?, ?, ?) + stmt = new ParameterizedSQL( + `CALL vn.timeControl_calculateByUser(?, ?, ?) `, [args.workerId, started, ended]); - stmts.push(stmt); + stmts.push(stmt); - stmt = new ParameterizedSQL( - `CALL vn.timeBusiness_calculateByUser(?, ?, ?) + 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); + 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); + 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.timeControl_calculateAll(?, ?)`, [started, ended]); + stmts.push(stmt); - stmt = new ParameterizedSQL(`CALL vn.timeBusiness_calculateAll(?, ?)`, [started, ended]); - stmts.push(stmt); - } + stmt = new ParameterizedSQL(`CALL vn.timeBusiness_calculateAll(?, ?)`, [started, ended]); + stmts.push(stmt); + } - stmts.push(`CREATE TEMPORARY TABLE tmp.timeControlCalculate1 + stmts.push(`CREATE TEMPORARY TABLE tmp.timeControlCalculate1 SELECT * FROM tmp.timeControlCalculate`); - stmts.push(`CREATE TEMPORARY TABLE tmp.timeBusinessCalculate1 + stmts.push(`CREATE TEMPORARY TABLE tmp.timeBusinessCalculate1 SELECT * FROM tmp.timeBusinessCalculate`); - stmt = new ParameterizedSQL(` + stmt = new ParameterizedSQL(` SELECT CONCAT(u.name, '@verdnatura.es') receiver, u.id workerFk, tb.dated, @@ -173,100 +164,80 @@ module.exports = Self => { AND w.businessFk ORDER BY u.id, tb.dated `, [args.workerId]); - const index = stmts.push(stmt) - 1; + const index = stmts.push(stmt) - 1; - const sql = ParameterizedSQL.join(stmts, ';'); - const days = await conn.executeStmt(sql, myOptions); + const sql = ParameterizedSQL.join(stmts, ';'); + const days = await conn.executeStmt(sql, myOptions); - let previousWorkerFk = days[index][0].workerFk; - let previousReceiver = days[index][0].receiver; + let previousWorkerFk = days[index][0].workerFk; + let previousReceiver = days[index][0].receiver; - for (let day of days[index]) { - 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 >= 5) { + 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(9), + timed: timed.setHours(8), manual: true, - direction: 'middle', + direction: 'in', isSendMail: true }, myOptions); - await models.WorkerTimeControl.create({ - userFk: day.workerFk, - timed: timed.setHours(9, 20), - manual: true, - direction: 'middle', - isSendMail: true - }, myOptions); - } + if (day.timeWorkDecimal >= 5) { + await models.WorkerTimeControl.create({ + userFk: day.workerFk, + timed: timed.setHours(9), + 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 + await models.WorkerTimeControl.create({ + userFk: day.workerFk, + timed: timed.setHours(9, 20), + manual: true, + direction: 'middle', + isSendMail: true + }, myOptions); } - }, 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); + 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); - const end = new Date(); - const [endHours, endMinutes, endSeconds] = getTime(journey.end); - end.setHours(endHours, endMinutes, endSeconds); - - 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); + let timeTableDecimalInSeconds = 0; + for (let journey of journeys) { + const start = new Date(); 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); + start.setHours(startHours, startMinutes, startSeconds); + const end = new Date(); 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) { + end.setHours(endHours, endMinutes, endSeconds); + + 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({ @@ -276,83 +247,140 @@ module.exports = Self => { isSendMail: true }, myOptions); - const [hoursWork, minutesWork, secondsWork] = getTime(day.timeWorkSexagesimal); + const [endHours, endMinutes, endSeconds] = getTime(journey.end); await models.WorkerTimeControl.create({ userFk: day.workerFk, - timed: timed.setHours( - startHours + hoursWork, startMinutes + minutesWork, startSeconds + secondsWork - ), + 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 >= 5) { + 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 (day.timeWorkDecimal >= 5) { - 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); + if (firstWorkerTimeControl) + firstWorkerTimeControl.updateAttribute('direction', 'in', myOptions); - await models.WorkerTimeControl.create({ - userFk: day.workerFk, - timed: timed.setHours(startHours + 1, startMinutes + 20, startSeconds), - manual: true, - isSendMail: true - }, 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 timed = new Date(day.dated); - const firstWorkerTimeControl = await models.WorkerTimeControl.findOne({ + const lastDay = days[index][days[index].length - 1]; + if (day.workerFk != previousWorkerFk || day == lastDay) { + const salix = await models.Url.findOne({ where: { - userFk: day.workerFk, - timed: {between: [timed.setHours(0, 0, 0, 0), timed.setHours(23, 59, 59, 999)]} - }, - order: 'timed ASC', - limit: 1 + appName: 'salix', + environment: process.env.NODE_ENV || 'dev' + } }, 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', - limit: 1 + 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); - if (lastWorkerTimeControl) lastWorkerTimeControl.updateAttribute('direction', 'out', myOptions); + await models.WorkerTimeControlMail.create({ + workerFk: previousWorkerFk, + week: args.week, + year: args.year + }, myOptions); + + previousWorkerFk = day.workerFk; + previousReceiver = day.receiver; } } - const origin = ctx.req.headers.origin; - const lastDay = days[index][days[index].length - 1]; - if (day.workerFk != previousWorkerFk || day == lastDay) { - await models.Mail.create({ - receiver: previousReceiver, - subject: $t('Record of hours week', { - week: args.week, - year: args.year - }), - body: `${origin}/#!/worker/${previousWorkerFk}/time-control?timestamp=` - }); - previousWorkerFk = day.workerFk; - previousReceiver = day.receiver; - } + if (tx) await tx.commit(); + } catch (e) { + if (tx) await tx.rollback(); + throw e; } - return true; }; + + 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)]; + } }; diff --git a/modules/worker/back/methods/worker-time-control/updateWorkerTimeControlMail.js b/modules/worker/back/methods/worker-time-control/updateWorkerTimeControlMail.js new file mode 100644 index 0000000000..23dcce262b --- /dev/null +++ b/modules/worker/back/methods/worker-time-control/updateWorkerTimeControlMail.js @@ -0,0 +1,61 @@ +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: 'emailResponse', + 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 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); + + return workerTimeControlMail.updateAttributes({ + state: args.state, + emailResponse: args.emailResponse || null + }, myOptions); + }; +}; diff --git a/modules/worker/back/models/worker-time-control-mail.json b/modules/worker/back/models/worker-time-control-mail.json index daf3d5155e..0a0c2bf9a7 100644 --- a/modules/worker/back/models/worker-time-control-mail.json +++ b/modules/worker/back/models/worker-time-control-mail.json @@ -9,8 +9,7 @@ "properties": { "id": { "id": true, - "type": "number", - "required": true + "type": "number" }, "workerFk": { "type": "number" diff --git a/modules/worker/back/models/worker-time-control.js b/modules/worker/back/models/worker-time-control.js index 928606b7d5..9f802511aa 100644 --- a/modules/worker/back/models/worker-time-control.js +++ b/modules/worker/back/models/worker-time-control.js @@ -6,6 +6,7 @@ module.exports = 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') diff --git a/modules/worker/front/time-control/index.html b/modules/worker/front/time-control/index.html index 1b60dcce08..681d420d03 100644 --- a/modules/worker/front/time-control/index.html +++ b/modules/worker/front/time-control/index.html @@ -81,11 +81,11 @@ + ng-click="$ctrl.isSatisfied()"> + ng-click="reason.show()"> @@ -160,4 +160,21 @@ ng-click="$ctrl.save()"> - \ No newline at end of file + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/worker/front/time-control/index.js b/modules/worker/front/time-control/index.js index ebf70e886d..9bd9bb5717 100644 --- a/modules/worker/front/time-control/index.js +++ b/modules/worker/front/time-control/index.js @@ -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', + emailResponse: 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; diff --git a/modules/worker/front/time-control/locale/es.yml b/modules/worker/front/time-control/locale/es.yml index cace38291f..2a3bffc002 100644 --- a/modules/worker/front/time-control/locale/es.yml +++ b/modules/worker/front/time-control/locale/es.yml @@ -12,4 +12,5 @@ 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 Satisfied: Conforme -Not satisfied: No conforme \ No newline at end of file +Not satisfied: No conforme +Reason: Motivo \ No newline at end of file From 04f165d9018a4092a2d38035021fafcfff799db6 Mon Sep 17 00:00:00 2001 From: vicent Date: Wed, 2 Nov 2022 14:23:35 +0100 Subject: [PATCH 08/21] solved conflits --- db/dump/fixtures.sql | 3 --- 1 file changed, 3 deletions(-) diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index cb0037dd9d..c347d827ba 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -2715,7 +2715,6 @@ UPDATE `account`.`user` SET `hasGrant` = 1 WHERE `id` = 66; -<<<<<<< HEAD INSERT INTO `postgresql`.`journey` (`journey_id`, `day_id`, `start`, `end`, `business_id`) VALUES (1, 1, '09:00:00', '13:00:00', 18), @@ -2730,8 +2729,6 @@ VALUES (10, 4, '07:00:00', '14:00:00', 19), (11, 5, '08:00:00', '13:00:00', 19); -======= 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'); ->>>>>>> 6ed5ef7d1b79034791af9ecba79ecb9e3194b1ee From ab739f412f0fa60432a06a71b66305bf6fc8b06f Mon Sep 17 00:00:00 2001 From: vicent Date: Thu, 3 Nov 2022 15:00:55 +0100 Subject: [PATCH 09/21] feat: add test --- .../worker-time-control-mail/checkInbox.js | 182 ------ .../methods/worker-time-control/sendMail.js | 15 +- .../specs/sendMail.spec.js | 529 ++++++++++++++++++ .../back/models/worker-time-control-mail.js | 3 - 4 files changed, 537 insertions(+), 192 deletions(-) delete mode 100644 modules/worker/back/methods/worker-time-control-mail/checkInbox.js create mode 100644 modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js delete mode 100644 modules/worker/back/models/worker-time-control-mail.js diff --git a/modules/worker/back/methods/worker-time-control-mail/checkInbox.js b/modules/worker/back/methods/worker-time-control-mail/checkInbox.js deleted file mode 100644 index 3e64a985ab..0000000000 --- a/modules/worker/back/methods/worker-time-control-mail/checkInbox.js +++ /dev/null @@ -1,182 +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); - const bodyPositionIndex = (bodyPositionOK.index == 0 || bodyPositionOK.index == 122); - if (bodyPositionOK != null && bodyPositionIndex) - 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 - }); - } -}; diff --git a/modules/worker/back/methods/worker-time-control/sendMail.js b/modules/worker/back/methods/worker-time-control/sendMail.js index c87df5ceaa..57f58ef220 100644 --- a/modules/worker/back/methods/worker-time-control/sendMail.js +++ b/modules/worker/back/methods/worker-time-control/sendMail.js @@ -225,11 +225,11 @@ module.exports = Self => { for (let journey of journeys) { const start = new Date(); const [startHours, startMinutes, startSeconds] = getTime(journey.start); - start.setHours(startHours, startMinutes, startSeconds); + start.setHours(startHours, startMinutes, startSeconds, 0); const end = new Date(); const [endHours, endMinutes, endSeconds] = getTime(journey.end); - end.setHours(endHours, endMinutes, endSeconds); + end.setHours(endHours, endMinutes, endSeconds, 0); const result = (end - start) / 1000; timeTableDecimalInSeconds += result; @@ -237,6 +237,7 @@ module.exports = Self => { for (let journey of journeys) { const timeTableDecimal = timeTableDecimalInSeconds / 3600; + console.log(timeTableDecimal); if (day.timeWorkDecimal == timeTableDecimal) { const timed = new Date(day.dated); const [startHours, startMinutes, startSeconds] = getTime(journey.start); @@ -349,11 +350,11 @@ module.exports = Self => { body: `${salix.url}worker/${previousWorkerFk}/time-control?timestamp=${timestamp}` }, myOptions); - await models.WorkerTimeControlMail.create({ - workerFk: previousWorkerFk, - week: args.week, - year: args.year - }, myOptions); + // await models.WorkerTimeControlMail.create({ + // workerFk: previousWorkerFk, + // week: args.week, + // year: args.year + // }, myOptions); 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 new file mode 100644 index 0000000000..978891bd01 --- /dev/null +++ b/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js @@ -0,0 +1,529 @@ +/* eslint max-len: ["error", { "code": 150 }]*/ +const models = require('vn-loopback/server/server').models; +const LoopBackContext = require('loopback-context'); + +describe('workerTimeControl sendMail()', () => { + const HHRRId = 37; + const teamBossId = 13; + const employeeId = 1; + const salesPersonId = 1106; + const salesBossId = 19; + const hankPymId = 1107; + const jessicaJonesId = 1110; + const monday = 1; + const tuesday = 2; + const thursday = 4; + const friday = 5; + const saturday = 6; + const sunday = 7; + const activeCtx = { + accessToken: {userId: 50}, + }; + const ctx = {req: activeCtx}; + + beforeAll(() => { + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + }); + + describe('WorkerTimeControl_clockIn calls', () => { + it('should fail to add a time entry if the target user has an absence that day', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = hankPymId; + const date = new Date(); + date.setDate(date.getDate() - 16); + date.setHours(8, 0, 0); + let error; + + const tx = await models.WorkerTimeControl.beginTransaction({}); + const options = {transaction: tx}; + try { + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toBe(`No está permitido trabajar`); + }); + + it('should fail to add a time entry for a worker without an existing contract', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = hankPymId; + const date = new Date(); + date.setFullYear(date.getFullYear() - 2); + let error; + + const tx = await models.WorkerTimeControl.beginTransaction({}); + try { + const options = {transaction: tx}; + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toBe(`No hay un contrato en vigor`); + }); + + describe('direction errors', () => { + it('should throw an error when trying "in" direction twice', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = hankPymId; + + let date = new Date(); + date.setDate(date.getDate() - 21); + date = weekDay(date, monday); + let error; + + const tx = await models.WorkerTimeControl.beginTransaction({}); + const options = {transaction: tx}; + date.setHours(8, 0, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + try { + date.setHours(10, 0, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toBe(`Dirección incorrecta`); + }); + + it('should throw an error when trying "in" direction after insert "in" and "middle"', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = hankPymId; + + let date = new Date(); + date.setDate(date.getDate() - 21); + date = weekDay(date, monday); + let error; + + const tx = await models.WorkerTimeControl.beginTransaction({}); + const options = {transaction: tx}; + + date.setHours(8, 0, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + date.setHours(9, 0, 0); + ctx.args = {timed: date, direction: 'middle'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + try { + date.setHours(10, 0, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toBe(`Dirección incorrecta`); + }); + + it('Should throw an error when trying "out" before closing a "middle" couple', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = hankPymId; + + let date = new Date(); + date.setDate(date.getDate() - 21); + date = weekDay(date, monday); + let error; + + const tx = await models.WorkerTimeControl.beginTransaction({}); + const options = {transaction: tx}; + + date.setHours(8, 0, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + date.setHours(9, 0, 0); + ctx.args = {timed: date, direction: 'middle'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + try { + date.setHours(10, 0, 0); + ctx.args = {timed: date, direction: 'out'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toBe(`Dirección incorrecta`); + }); + + it('should throw an error when trying "middle" after "out"', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = hankPymId; + + let date = new Date(); + date.setDate(date.getDate() - 21); + date = weekDay(date, monday); + let error; + + const tx = await models.WorkerTimeControl.beginTransaction({}); + const options = {transaction: tx}; + + date.setHours(8, 0, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + date.setHours(9, 0, 0); + ctx.args = {timed: date, direction: 'out'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + try { + date.setHours(10, 0, 0); + ctx.args = {timed: date, direction: 'middle'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toBe(`Dirección incorrecta`); + }); + + it('should throw an error when trying "out" direction twice', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = hankPymId; + + let date = new Date(); + date.setDate(date.getDate() - 21); + date = weekDay(date, monday); + let error; + + const tx = await models.WorkerTimeControl.beginTransaction({}); + const options = {transaction: tx}; + + date.setHours(8, 0, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + date.setHours(9, 0, 0); + ctx.args = {timed: date, direction: 'out'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + try { + date.setHours(10, 0, 0); + ctx.args = {timed: date, direction: 'out'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toBe(`Dirección incorrecta`); + }); + }); + + describe('12h rest', () => { + it('should throw an error when the 12h rest is not fulfilled yet', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = hankPymId; + + let date = new Date(); + date.setDate(date.getDate() - 21); + date = weekDay(date, monday); + let error; + + const tx = await models.WorkerTimeControl.beginTransaction({}); + const options = {transaction: tx}; + + date.setHours(8, 0, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + date.setHours(16, 0, 0); + ctx.args = {timed: date, direction: 'out'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + try { + date = weekDay(date, tuesday); + date.setHours(4, 0, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toBe(`Descanso diario 12h.`); + }); + + it('should not fail as the 12h rest is fulfilled', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = hankPymId; + + let date = new Date(); + date.setDate(date.getDate() - 21); + date = weekDay(date, monday); + let error; + + const tx = await models.WorkerTimeControl.beginTransaction({}); + const options = {transaction: tx}; + + date.setHours(8, 0, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + date.setHours(16, 0, 0); + ctx.args = {timed: date, direction: 'out'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + try { + date = weekDay(date, tuesday); + date.setHours(4, 1, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error).not.toBeDefined; + }); + }); + + describe('for 3500kg drivers with enforced 9h rest', () => { + it('should throw an error when the 9h enforced rest is not fulfilled', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = jessicaJonesId; + + let date = new Date(); + date.setDate(date.getDate() - 21); + date = weekDay(date, monday); + let error; + + const tx = await models.WorkerTimeControl.beginTransaction({}); + const options = {transaction: tx}; + + date.setHours(8, 0, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + date.setHours(16, 0, 0); + ctx.args = {timed: date, direction: 'out'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + try { + date = weekDay(date, tuesday); + date.setHours(1, 0, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toBe(`Descanso diario 9h.`); + }); + + it('should not fail when the 9h enforced rest is fulfilled', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = jessicaJonesId; + + let date = new Date(); + date.setDate(date.getDate() - 21); + date = weekDay(date, monday); + let error; + + const tx = await models.WorkerTimeControl.beginTransaction({}); + const options = {transaction: tx}; + + date.setHours(8, 0, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + date.setHours(16, 0, 0); + ctx.args = {timed: date, direction: 'out'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + try { + date = weekDay(date, tuesday); + date.setHours(1, 1, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error).not.toBeDefined; + }); + }); + + describe('for 36h weekly rest', () => { + it('should throw an error when the 36h weekly rest is not fulfilled', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = hankPymId; + + let date = new Date(); + date.setMonth(date.getMonth() - 2); + date.setDate(1); + let error; + + const tx = await models.WorkerTimeControl.beginTransaction({}); + const options = {transaction: tx}; + + await populateWeek(date, monday, sunday, ctx, workerId, options); + date = nextWeek(date); + await populateWeek(date, monday, thursday, ctx, workerId, options); + date = weekDay(date, friday); + date.setHours(7, 59, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + try { + date.setHours(8, 1, 0); + ctx.args = {timed: date, direction: 'out'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toBe(`Descanso semanal 36h. / 72h.`); + }); + + it('should throw an error when the 36h weekly rest is not fulfilled again', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = hankPymId; + + let date = new Date(); + date.setMonth(date.getMonth() - 2); + date.setDate(1); + let error; + + const tx = await models.WorkerTimeControl.beginTransaction({}); + const options = {transaction: tx}; + + await populateWeek(date, monday, sunday, ctx, workerId, options); + date = nextWeek(date); + await populateWeek(date, monday, thursday, ctx, workerId, options); + + try { + date = weekDay(date, saturday); + date.setHours(3, 59, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toBe(`Descanso semanal 36h. / 72h.`); + }); + }); + + describe('for 72h weekly rest', () => { + it('should throw when the 72h weekly rest is not fulfilled yet', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = hankPymId; + + let date = new Date(); + date.setMonth(date.getMonth() - 2); + date.setDate(1); + let error; + + const tx = await models.WorkerTimeControl.beginTransaction({}); + const options = {transaction: tx}; + + await populateWeek(date, monday, sunday, ctx, workerId, options); + date = nextWeek(date); + await populateWeek(date, monday, thursday, ctx, workerId, options); + date = nextWeek(date); + await populateWeek(date, monday, friday, ctx, workerId, options); + date = nextWeek(date); + await populateWeek(date, monday, saturday, ctx, workerId, options); + date = nextWeek(date); + await populateWeek(date, monday, saturday, ctx, workerId, options); + date = lastWeek(date); + + try { + date = weekDay(date, sunday); + date.setHours(8, 0, 0); + ctx.args = {timed: date, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toBe(`Descanso semanal 36h. / 72h.`); + }); + }); + }); +}); + +function weekDay(date, dayToSet) { + const currentDay = date.getDay(); + const distance = dayToSet - currentDay; + + date.setDate(date.getDate() + distance); + return date; +} + +function nextWeek(date) { + const sunday = 7; + const currentDay = date.getDay(); + let newDate = date; + if (currentDay != 0) + newDate = weekDay(date, sunday); + + newDate.setDate(newDate.getDate() + 1); + return newDate; +} + +function lastWeek(date) { + const monday = 1; + newDate = weekDay(date, monday); + + newDate.setDate(newDate.getDate() - 1); + return newDate; +} + +async function populateWeek(date, dayStart, dayEnd, ctx, workerId, options) { + const dateStart = new Date(weekDay(date, dayStart)); + const dateEnd = new Date(dateStart); + dateEnd.setDate(dateStart.getDate() + dayEnd); + + for (let i = dayStart; i <= dayEnd; i++) { + dateStart.setHours(8, 0, 0); + ctx.args = {timed: dateStart, direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + dateStart.setHours(16, 0, 0); + ctx.args = {timed: dateStart, direction: 'out'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + dateStart.setDate(dateStart.getDate() + 1); + } +} diff --git a/modules/worker/back/models/worker-time-control-mail.js b/modules/worker/back/models/worker-time-control-mail.js deleted file mode 100644 index 36f3851b6d..0000000000 --- a/modules/worker/back/models/worker-time-control-mail.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = Self => { - require('../methods/worker-time-control-mail/checkInbox')(Self); -}; From 626355d251f396fa4213d5dabcc388e096412f64 Mon Sep 17 00:00:00 2001 From: vicent Date: Fri, 4 Nov 2022 12:37:07 +0100 Subject: [PATCH 10/21] fix: sustituido insert por insert ignore --- .../worker/back/methods/worker-time-control/sendMail.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/modules/worker/back/methods/worker-time-control/sendMail.js b/modules/worker/back/methods/worker-time-control/sendMail.js index 57f58ef220..1a57b5a052 100644 --- a/modules/worker/back/methods/worker-time-control/sendMail.js +++ b/modules/worker/back/methods/worker-time-control/sendMail.js @@ -350,11 +350,9 @@ module.exports = Self => { body: `${salix.url}worker/${previousWorkerFk}/time-control?timestamp=${timestamp}` }, myOptions); - // await models.WorkerTimeControlMail.create({ - // workerFk: previousWorkerFk, - // week: args.week, - // year: args.year - // }, 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; From 98cc2b58664b3144618e360fac1b283ba145bdaa Mon Sep 17 00:00:00 2001 From: vicent Date: Fri, 4 Nov 2022 13:41:38 +0100 Subject: [PATCH 11/21] refactor: endpoint --- db/dump/fixtures.sql | 13 ------------- .../methods/worker-time-control/sendMail.js | 17 ++++------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index c347d827ba..8160ae922d 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -2715,19 +2715,6 @@ UPDATE `account`.`user` SET `hasGrant` = 1 WHERE `id` = 66; -INSERT INTO `postgresql`.`journey` (`journey_id`, `day_id`, `start`, `end`, `business_id`) -VALUES - (1, 1, '09:00:00', '13:00:00', 18), - (2, 1, '14:00:00', '19:00:00', 18), - (3, 3, '12:30:00', '19:00:00', 18), - (4, 4, '01:30:00', '07:30:00', 18), - (5, 5, '01:00:00', '09:00:00', 18), - (6, 6, '02:00:00', '08:00:00', 18), - (7, 1, '07:00:00', '12:00:00', 19), - (8, 2, '09:00:00', '17:00:00', 19), - (9, 3, '15:00:00', '19:00:00', 19), - (10, 4, '07:00:00', '14:00:00', 19), - (11, 5, '08:00:00', '13:00:00', 19); INSERT INTO `vn`.`osTicketConfig` (`id`, `host`, `user`, `password`, `oldStatus`, `newStatusId`, `day`, `comment`, `hostDb`, `userDb`, `passwordDb`, `portDb`, `responseType`, `fromEmailId`, `replyTo`) VALUES diff --git a/modules/worker/back/methods/worker-time-control/sendMail.js b/modules/worker/back/methods/worker-time-control/sendMail.js index 1a57b5a052..243b10cf69 100644 --- a/modules/worker/back/methods/worker-time-control/sendMail.js +++ b/modules/worker/back/methods/worker-time-control/sendMail.js @@ -72,9 +72,6 @@ module.exports = Self => { stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate'); stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeBusinessCalculate'); - stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate1'); - stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeBusinessCalculate1'); - stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.error'); if (args.workerId) { await models.WorkerTimeControl.destroyAll({ @@ -122,12 +119,6 @@ module.exports = Self => { stmts.push(stmt); } - stmts.push(`CREATE TEMPORARY TABLE tmp.timeControlCalculate1 - SELECT * FROM tmp.timeControlCalculate`); - - stmts.push(`CREATE TEMPORARY TABLE tmp.timeBusinessCalculate1 - SELECT * FROM tmp.timeBusinessCalculate`); - stmt = new ParameterizedSQL(` SELECT CONCAT(u.name, '@verdnatura.es') receiver, u.id workerFk, @@ -147,13 +138,12 @@ module.exports = Self => { 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 - LEFT JOIN user u2 ON u2.id = w.bossFk 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.timeBusinessCalculate1 tb - LEFT JOIN tmp.timeControlCalculate1 tc ON tc.userFk = tb.userFk + 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 @@ -360,11 +350,12 @@ module.exports = Self => { } if (tx) await tx.commit(); + + return true; } catch (e) { if (tx) await tx.rollback(); throw e; } - return true; }; function getStartDateOfWeekNumber(week, year) { From 892094f85c6b622fc743199799fe53563a1493e8 Mon Sep 17 00:00:00 2001 From: vicent Date: Fri, 4 Nov 2022 13:41:45 +0100 Subject: [PATCH 12/21] feat: add backTest --- .../specs/sendMail.spec.js | 544 +----------------- 1 file changed, 25 insertions(+), 519 deletions(-) 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 978891bd01..4411fa576a 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 @@ -1,529 +1,35 @@ /* eslint max-len: ["error", { "code": 150 }]*/ const models = require('vn-loopback/server/server').models; -const LoopBackContext = require('loopback-context'); -describe('workerTimeControl sendMail()', () => { - const HHRRId = 37; - const teamBossId = 13; - const employeeId = 1; - const salesPersonId = 1106; - const salesBossId = 19; - const hankPymId = 1107; - const jessicaJonesId = 1110; - const monday = 1; - const tuesday = 2; - const thursday = 4; - const friday = 5; - const saturday = 6; - const sunday = 7; - const activeCtx = { - accessToken: {userId: 50}, +fdescribe('workerTimeControl sendMail()', () => { + const workerId = 18; + const ctx = { + req: { + __: value => { + return value; + } + }, + args: {} + }; - const ctx = {req: activeCtx}; - beforeAll(() => { - spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ - active: activeCtx - }); - }); + it('should...', async() => { + const tx = await models.WorkerTimeControl.beginTransaction({}); - describe('WorkerTimeControl_clockIn calls', () => { - it('should fail to add a time entry if the target user has an absence that day', async() => { - activeCtx.accessToken.userId = salesBossId; - const workerId = hankPymId; - const date = new Date(); - date.setDate(date.getDate() - 16); - date.setHours(8, 0, 0); - let error; - - const tx = await models.WorkerTimeControl.beginTransaction({}); + try { const options = {transaction: tx}; - try { - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - error = e; - } - - expect(error.message).toBe(`No está permitido trabajar`); - }); - - it('should fail to add a time entry for a worker without an existing contract', async() => { - activeCtx.accessToken.userId = salesBossId; - const workerId = hankPymId; - const date = new Date(); - date.setFullYear(date.getFullYear() - 2); - let error; - - const tx = await models.WorkerTimeControl.beginTransaction({}); - try { - const options = {transaction: tx}; - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - error = e; - } - - expect(error.message).toBe(`No hay un contrato en vigor`); - }); - - describe('direction errors', () => { - it('should throw an error when trying "in" direction twice', async() => { - activeCtx.accessToken.userId = salesBossId; - const workerId = hankPymId; - - let date = new Date(); - date.setDate(date.getDate() - 21); - date = weekDay(date, monday); - let error; - - const tx = await models.WorkerTimeControl.beginTransaction({}); - const options = {transaction: tx}; - date.setHours(8, 0, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - try { - date.setHours(10, 0, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - error = e; - } - - expect(error.message).toBe(`Dirección incorrecta`); - }); - - it('should throw an error when trying "in" direction after insert "in" and "middle"', async() => { - activeCtx.accessToken.userId = salesBossId; - const workerId = hankPymId; - - let date = new Date(); - date.setDate(date.getDate() - 21); - date = weekDay(date, monday); - let error; - - const tx = await models.WorkerTimeControl.beginTransaction({}); - const options = {transaction: tx}; - - date.setHours(8, 0, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - date.setHours(9, 0, 0); - ctx.args = {timed: date, direction: 'middle'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - try { - date.setHours(10, 0, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - error = e; - } - - expect(error.message).toBe(`Dirección incorrecta`); - }); - - it('Should throw an error when trying "out" before closing a "middle" couple', async() => { - activeCtx.accessToken.userId = salesBossId; - const workerId = hankPymId; - - let date = new Date(); - date.setDate(date.getDate() - 21); - date = weekDay(date, monday); - let error; - - const tx = await models.WorkerTimeControl.beginTransaction({}); - const options = {transaction: tx}; - - date.setHours(8, 0, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - date.setHours(9, 0, 0); - ctx.args = {timed: date, direction: 'middle'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - try { - date.setHours(10, 0, 0); - ctx.args = {timed: date, direction: 'out'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - error = e; - } - - expect(error.message).toBe(`Dirección incorrecta`); - }); - - it('should throw an error when trying "middle" after "out"', async() => { - activeCtx.accessToken.userId = salesBossId; - const workerId = hankPymId; - - let date = new Date(); - date.setDate(date.getDate() - 21); - date = weekDay(date, monday); - let error; - - const tx = await models.WorkerTimeControl.beginTransaction({}); - const options = {transaction: tx}; - - date.setHours(8, 0, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - date.setHours(9, 0, 0); - ctx.args = {timed: date, direction: 'out'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - try { - date.setHours(10, 0, 0); - ctx.args = {timed: date, direction: 'middle'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - error = e; - } - - expect(error.message).toBe(`Dirección incorrecta`); - }); - - it('should throw an error when trying "out" direction twice', async() => { - activeCtx.accessToken.userId = salesBossId; - const workerId = hankPymId; - - let date = new Date(); - date.setDate(date.getDate() - 21); - date = weekDay(date, monday); - let error; - - const tx = await models.WorkerTimeControl.beginTransaction({}); - const options = {transaction: tx}; - - date.setHours(8, 0, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - date.setHours(9, 0, 0); - ctx.args = {timed: date, direction: 'out'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - try { - date.setHours(10, 0, 0); - ctx.args = {timed: date, direction: 'out'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - error = e; - } - - expect(error.message).toBe(`Dirección incorrecta`); - }); - }); - - describe('12h rest', () => { - it('should throw an error when the 12h rest is not fulfilled yet', async() => { - activeCtx.accessToken.userId = salesBossId; - const workerId = hankPymId; - - let date = new Date(); - date.setDate(date.getDate() - 21); - date = weekDay(date, monday); - let error; - - const tx = await models.WorkerTimeControl.beginTransaction({}); - const options = {transaction: tx}; - - date.setHours(8, 0, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - date.setHours(16, 0, 0); - ctx.args = {timed: date, direction: 'out'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - try { - date = weekDay(date, tuesday); - date.setHours(4, 0, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - error = e; - } - - expect(error.message).toBe(`Descanso diario 12h.`); - }); - - it('should not fail as the 12h rest is fulfilled', async() => { - activeCtx.accessToken.userId = salesBossId; - const workerId = hankPymId; - - let date = new Date(); - date.setDate(date.getDate() - 21); - date = weekDay(date, monday); - let error; - - const tx = await models.WorkerTimeControl.beginTransaction({}); - const options = {transaction: tx}; - - date.setHours(8, 0, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - date.setHours(16, 0, 0); - ctx.args = {timed: date, direction: 'out'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - try { - date = weekDay(date, tuesday); - date.setHours(4, 1, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - error = e; - } - - expect(error).not.toBeDefined; - }); - }); - - describe('for 3500kg drivers with enforced 9h rest', () => { - it('should throw an error when the 9h enforced rest is not fulfilled', async() => { - activeCtx.accessToken.userId = salesBossId; - const workerId = jessicaJonesId; - - let date = new Date(); - date.setDate(date.getDate() - 21); - date = weekDay(date, monday); - let error; - - const tx = await models.WorkerTimeControl.beginTransaction({}); - const options = {transaction: tx}; - - date.setHours(8, 0, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - date.setHours(16, 0, 0); - ctx.args = {timed: date, direction: 'out'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - try { - date = weekDay(date, tuesday); - date.setHours(1, 0, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - error = e; - } - - expect(error.message).toBe(`Descanso diario 9h.`); - }); - - it('should not fail when the 9h enforced rest is fulfilled', async() => { - activeCtx.accessToken.userId = salesBossId; - const workerId = jessicaJonesId; - - let date = new Date(); - date.setDate(date.getDate() - 21); - date = weekDay(date, monday); - let error; - - const tx = await models.WorkerTimeControl.beginTransaction({}); - const options = {transaction: tx}; - - date.setHours(8, 0, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - date.setHours(16, 0, 0); - ctx.args = {timed: date, direction: 'out'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - try { - date = weekDay(date, tuesday); - date.setHours(1, 1, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - error = e; - } - - expect(error).not.toBeDefined; - }); - }); - - describe('for 36h weekly rest', () => { - it('should throw an error when the 36h weekly rest is not fulfilled', async() => { - activeCtx.accessToken.userId = salesBossId; - const workerId = hankPymId; - - let date = new Date(); - date.setMonth(date.getMonth() - 2); - date.setDate(1); - let error; - - const tx = await models.WorkerTimeControl.beginTransaction({}); - const options = {transaction: tx}; - - await populateWeek(date, monday, sunday, ctx, workerId, options); - date = nextWeek(date); - await populateWeek(date, monday, thursday, ctx, workerId, options); - date = weekDay(date, friday); - date.setHours(7, 59, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - try { - date.setHours(8, 1, 0); - ctx.args = {timed: date, direction: 'out'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - error = e; - } - - expect(error.message).toBe(`Descanso semanal 36h. / 72h.`); - }); - - it('should throw an error when the 36h weekly rest is not fulfilled again', async() => { - activeCtx.accessToken.userId = salesBossId; - const workerId = hankPymId; - - let date = new Date(); - date.setMonth(date.getMonth() - 2); - date.setDate(1); - let error; - - const tx = await models.WorkerTimeControl.beginTransaction({}); - const options = {transaction: tx}; - - await populateWeek(date, monday, sunday, ctx, workerId, options); - date = nextWeek(date); - await populateWeek(date, monday, thursday, ctx, workerId, options); - - try { - date = weekDay(date, saturday); - date.setHours(3, 59, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - error = e; - } - - expect(error.message).toBe(`Descanso semanal 36h. / 72h.`); - }); - }); - - describe('for 72h weekly rest', () => { - it('should throw when the 72h weekly rest is not fulfilled yet', async() => { - activeCtx.accessToken.userId = salesBossId; - const workerId = hankPymId; - - let date = new Date(); - date.setMonth(date.getMonth() - 2); - date.setDate(1); - let error; - - const tx = await models.WorkerTimeControl.beginTransaction({}); - const options = {transaction: tx}; - - await populateWeek(date, monday, sunday, ctx, workerId, options); - date = nextWeek(date); - await populateWeek(date, monday, thursday, ctx, workerId, options); - date = nextWeek(date); - await populateWeek(date, monday, friday, ctx, workerId, options); - date = nextWeek(date); - await populateWeek(date, monday, saturday, ctx, workerId, options); - date = nextWeek(date); - await populateWeek(date, monday, saturday, ctx, workerId, options); - date = lastWeek(date); - - try { - date = weekDay(date, sunday); - date.setHours(8, 0, 0); - ctx.args = {timed: date, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - error = e; - } - - expect(error.message).toBe(`Descanso semanal 36h. / 72h.`); - }); - }); + await models.WorkerTimeControl.sendMail(ctx, options); + const workerTimeControl = await models.WorkerTimeControl.find({ + where: {userFk: workerId} + }, options); + console.log(workerTimeControl); + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + + expect(result[1]).toEqual('text/plain'); }); }); -function weekDay(date, dayToSet) { - const currentDay = date.getDay(); - const distance = dayToSet - currentDay; - - date.setDate(date.getDate() + distance); - return date; -} - -function nextWeek(date) { - const sunday = 7; - const currentDay = date.getDay(); - let newDate = date; - if (currentDay != 0) - newDate = weekDay(date, sunday); - - newDate.setDate(newDate.getDate() + 1); - return newDate; -} - -function lastWeek(date) { - const monday = 1; - newDate = weekDay(date, monday); - - newDate.setDate(newDate.getDate() - 1); - return newDate; -} - -async function populateWeek(date, dayStart, dayEnd, ctx, workerId, options) { - const dateStart = new Date(weekDay(date, dayStart)); - const dateEnd = new Date(dateStart); - dateEnd.setDate(dateStart.getDate() + dayEnd); - - for (let i = dayStart; i <= dayEnd; i++) { - dateStart.setHours(8, 0, 0); - ctx.args = {timed: dateStart, direction: 'in'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - dateStart.setHours(16, 0, 0); - ctx.args = {timed: dateStart, direction: 'out'}; - await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); - dateStart.setDate(dateStart.getDate() + 1); - } -} From 6858de83a3969a071f91f72f85b9e169275f2861 Mon Sep 17 00:00:00 2001 From: vicent Date: Fri, 4 Nov 2022 14:55:47 +0100 Subject: [PATCH 13/21] feat: backTest --- .../specs/sendMail.spec.js | 120 +++++++++++++++++- 1 file changed, 117 insertions(+), 3 deletions(-) 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 4411fa576a..f05432ba21 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 @@ -13,23 +13,137 @@ fdescribe('workerTimeControl sendMail()', () => { }; - it('should...', async() => { + beforeAll(function() { + originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; + jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; + }); + + it('should fill time control of a worker...', 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); - console.log(workerTimeControl); + + 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; } + }); - expect(result[1]).toEqual('text/plain'); + it('should fill time control of a worker2...', 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 worker3...', async() => { + const workdayOf20Hours = 3; + 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', ?), + (3, 2, '12:30: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); + console.log(workerTimeControl); + + 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 worker4...', 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); + + 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', ?), + (3, 2, '12:30: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); + console.log(workerTimeControl); + + 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; + } + }); + + afterAll(function() { + jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; }); }); From ffa0ff650eb0321821fd221f86a142ad91a308ee Mon Sep 17 00:00:00 2001 From: vicent Date: Mon, 7 Nov 2022 12:21:14 +0100 Subject: [PATCH 14/21] feat: add time control --- db/dump/fixtures.sql | 1 - .../methods/worker-time-control/sendMail.js | 7 +-- .../specs/sendMail.spec.js | 48 ++----------------- modules/worker/back/model-config.json | 3 ++ .../models/worker-time-control-config.json | 18 +++++++ 5 files changed, 28 insertions(+), 49 deletions(-) create mode 100644 modules/worker/back/models/worker-time-control-config.json diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 8160ae922d..d875881b9e 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -2715,7 +2715,6 @@ UPDATE `account`.`user` SET `hasGrant` = 1 WHERE `id` = 66; - 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'); diff --git a/modules/worker/back/methods/worker-time-control/sendMail.js b/modules/worker/back/methods/worker-time-control/sendMail.js index 243b10cf69..2f9559b3a1 100644 --- a/modules/worker/back/methods/worker-time-control/sendMail.js +++ b/modules/worker/back/methods/worker-time-control/sendMail.js @@ -162,6 +162,8 @@ module.exports = Self => { 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 @@ -176,7 +178,7 @@ module.exports = Self => { isSendMail: true }, myOptions); - if (day.timeWorkDecimal >= 5) { + if (day.timeWorkDecimal >= workerTimeControlConfig.timeToBreakTime / 3600) { await models.WorkerTimeControl.create({ userFk: day.workerFk, timed: timed.setHours(9), @@ -227,7 +229,6 @@ module.exports = Self => { for (let journey of journeys) { const timeTableDecimal = timeTableDecimalInSeconds / 3600; - console.log(timeTableDecimal); if (day.timeWorkDecimal == timeTableDecimal) { const timed = new Date(day.dated); const [startHours, startMinutes, startSeconds] = getTime(journey.start); @@ -273,7 +274,7 @@ module.exports = Self => { } } - if (day.timeWorkDecimal >= 5) { + if (day.timeWorkDecimal >= workerTimeControlConfig.timeToBreakTime / 3600) { const minStart = journeys.reduce(function(prev, curr) { return curr.start < prev.start ? curr : prev; }); 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 f05432ba21..ec3012e825 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 @@ -1,4 +1,3 @@ -/* eslint max-len: ["error", { "code": 150 }]*/ const models = require('vn-loopback/server/server').models; fdescribe('workerTimeControl sendMail()', () => { @@ -18,7 +17,7 @@ fdescribe('workerTimeControl sendMail()', () => { jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; }); - it('should fill time control of a worker...', async() => { + it('should fill time control of a worker without records in Journey and and with rest', async() => { const tx = await models.WorkerTimeControl.beginTransaction({}); try { @@ -42,7 +41,7 @@ fdescribe('workerTimeControl sendMail()', () => { } }); - it('should fill time control of a worker2...', async() => { + it('should fill time control of a worker without records in Journey and and without rest', async() => { const workdayOf20Hours = 3; const tx = await models.WorkerTimeControl.beginTransaction({}); @@ -69,8 +68,7 @@ fdescribe('workerTimeControl sendMail()', () => { } }); - it('should fill time control of a worker3...', async() => { - const workdayOf20Hours = 3; + it('should fill time control of a worker with records in Journey', async() => { const tx = await models.WorkerTimeControl.beginTransaction({}); try { @@ -87,46 +85,6 @@ fdescribe('workerTimeControl sendMail()', () => { const workerTimeControl = await models.WorkerTimeControl.find({ where: {userFk: workerId} }, options); - console.log(workerTimeControl); - - 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 worker4...', 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); - - 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', ?), - (3, 2, '12:30: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); - console.log(workerTimeControl); expect(workerTimeControl[0].timed.getHours()).toEqual(9); expect(workerTimeControl[2].timed.getHours()).toEqual(10); diff --git a/modules/worker/back/model-config.json b/modules/worker/back/model-config.json index b2051069be..3f34165040 100644 --- a/modules/worker/back/model-config.json +++ b/modules/worker/back/model-config.json @@ -65,6 +65,9 @@ "WorkerLog": { "dataSource": "vn" }, + "WorkerTimeControlConfig": { + "dataSource": "vn" + }, "WorkerTimeControlParams": { "dataSource": "vn" }, diff --git a/modules/worker/back/models/worker-time-control-config.json b/modules/worker/back/models/worker-time-control-config.json new file mode 100644 index 0000000000..4c12ce5d73 --- /dev/null +++ b/modules/worker/back/models/worker-time-control-config.json @@ -0,0 +1,18 @@ +{ + "name": "WorkerTimeControlConfig", + "base": "VnModel", + "options": { + "mysql": { + "table": "workerTimeControlConfig" + } + }, + "properties": { + "id": { + "id": true, + "type": "number" + }, + "timeToBreakTime": { + "type": "number" + } + } +} \ No newline at end of file From 7fc18c89df0d506d178b95424148241f391d9747 Mon Sep 17 00:00:00 2001 From: vicent Date: Mon, 7 Nov 2022 13:57:54 +0100 Subject: [PATCH 15/21] feat: error handlers --- loopback/locale/es.json | 10 ++++--- .../specs/sendMail.spec.js | 2 +- .../updateWorkerTimeControlMail.js | 28 ++++++++++++++++++- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/loopback/locale/es.json b/loopback/locale/es.json index afcdf31ab7..1805682af3 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -237,7 +237,9 @@ "Modifiable user details only by an administrator": "Detalles de usuario modificables solo por un administrador", "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", - "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" +} \ No newline at end of file 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 ec3012e825..c9cbb5da88 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 @@ -1,6 +1,6 @@ const models = require('vn-loopback/server/server').models; -fdescribe('workerTimeControl sendMail()', () => { +describe('workerTimeControl sendMail()', () => { const workerId = 18; const ctx = { req: { diff --git a/modules/worker/back/methods/worker-time-control/updateWorkerTimeControlMail.js b/modules/worker/back/methods/worker-time-control/updateWorkerTimeControlMail.js index 23dcce262b..3bb2b90136 100644 --- a/modules/worker/back/methods/worker-time-control/updateWorkerTimeControlMail.js +++ b/modules/worker/back/methods/worker-time-control/updateWorkerTimeControlMail.js @@ -1,3 +1,4 @@ +const UserError = require('vn-loopback/util/user-error'); module.exports = Self => { Self.remoteMethodCtx('updateWorkerTimeControlMail', { description: 'Updates the state of WorkerTimeControlMail', @@ -39,6 +40,7 @@ module.exports = Self => { Self.updateWorkerTimeControlMail = async(ctx, options) => { const models = Self.app.models; const args = ctx.args; + const userId = ctx.req.accessToken.userId; const myOptions = {}; @@ -53,9 +55,33 @@ module.exports = Self => { } }, myOptions); - return workerTimeControlMail.updateAttributes({ + if (!workerTimeControlMail) throw new UserError(`There aren't records for this week`); + + const oldState = workerTimeControlMail.state; + const oldEmailResponse = workerTimeControlMail.emailResponse; + + if (oldState == args.state) throw new UserError('Already has this status'); + + await workerTimeControlMail.updateAttributes({ state: args.state, emailResponse: args.emailResponse || null }, myOptions); + + const logRecord = { + originFk: args.workerId, + userFk: userId, + action: 'update', + changedModel: 'WorkerTimeControlMail', + oldInstance: { + state: oldState, + emailResponse: oldEmailResponse + }, + newInstance: { + state: args.state, + emailResponse: args.emailResponse + } + }; + + return models.WorkerLog.create(logRecord, myOptions); }; }; From d34a4ae992ce7b44b1a3df124773aa87dd2e80c6 Mon Sep 17 00:00:00 2001 From: vicent Date: Tue, 8 Nov 2022 08:19:25 +0100 Subject: [PATCH 16/21] add translate --- loopback/locale/es.json | 260 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 240 insertions(+), 20 deletions(-) diff --git a/loopback/locale/es.json b/loopback/locale/es.json index f70c9b2e19..1805682af3 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -1,25 +1,245 @@ { - "Record of hours week": "Record of hours week", - "Invalid email": "Invalid email", - "Name cannot be blank": "Name cannot be blank", - "Swift / BIC cannot be empty": "Swift / BIC cannot be empty", - "Street cannot be empty": "Street cannot be empty", - "City cannot be empty": "City cannot be empty", - "Phone cannot be blank": "Phone cannot be blank", + "Phone format is invalid": "El formato del teléfono no es correcto", + "You are not allowed to change the credit": "No tienes privilegios para modificar el crédito", + "Unable to mark the equivalence surcharge": "No se puede marcar el recargo de equivalencia", + "The default consignee can not be unchecked": "No se puede desmarcar el consignatario predeterminado", + "Unable to default a disabled consignee": "No se puede poner predeterminado un consignatario desactivado", + "Can't be blank": "No puede estar en blanco", + "Invalid TIN": "NIF/CIF invalido", + "TIN must be unique": "El NIF/CIF debe ser único", + "A client with that Web User name already exists": "Ya existe un cliente con ese Usuario Web", + "Is invalid": "Is invalid", + "Quantity cannot be zero": "La cantidad no puede ser cero", + "Enter an integer different to zero": "Introduce un entero distinto de cero", + "Package cannot be blank": "El embalaje no puede estar en blanco", + "The company name must be unique": "La razón social debe ser única", + "Invalid email": "Correo electrónico inválido", + "The IBAN does not have the correct format": "El IBAN no tiene el formato correcto", + "That payment method requires an IBAN": "El método de pago seleccionado requiere un IBAN", + "That payment method requires a BIC": "El método de pago seleccionado requiere un BIC", + "State cannot be blank": "El estado no puede estar en blanco", + "Worker cannot be blank": "El trabajador no puede estar en blanco", + "Cannot change the payment method if no salesperson": "No se puede cambiar la forma de pago si no hay comercial asignado", + "can't be blank": "El campo no puede estar vacío", + "Observation type must be unique": "El tipo de observación no puede repetirse", "The credit must be an integer greater than or equal to zero": "The credit must be an integer greater than or equal to zero", - "The grade must be an integer greater than or equal to zero": "The grade must be an integer greater than or equal to zero", - "Description should have maximum of 45 characters": "Description should have maximum of 45 characters", - "Amount cannot be zero": "Amount cannot be zero", - "Period cannot be blank": "Period cannot be blank", - "Sample type cannot be blank": "Sample type cannot be blank", - "Cannot be blank": "Cannot be blank", - "The social name cannot be empty": "The social name cannot be empty", - "Concept cannot be blank": "Concept cannot be blank", - "Enter an integer different to zero": "Enter an integer different to zero", - "Package cannot be blank": "Package cannot be blank", - "State cannot be blank": "State cannot be blank", - "Worker cannot be blank": "Worker cannot be blank", - "Agency cannot be blank": "Agency cannot be blank", + "The grade must be similar to the last one": "El grade debe ser similar al último", + "Only manager can change the credit": "Solo el gerente puede cambiar el credito de este cliente", + "Name cannot be blank": "El nombre no puede estar en blanco", + "Phone cannot be blank": "El teléfono no puede estar en blanco", + "Period cannot be blank": "El periodo no puede estar en blanco", + "Choose a company": "Selecciona una empresa", + "Se debe rellenar el campo de texto": "Se debe rellenar el campo de texto", + "Description should have maximum of 45 characters": "La descripción debe tener maximo 45 caracteres", + "Cannot be blank": "El campo no puede estar en blanco", + "The grade must be an integer greater than or equal to zero": "El grade debe ser un entero mayor o igual a cero", + "Sample type cannot be blank": "El tipo de plantilla no puede quedar en blanco", + "Description cannot be blank": "Se debe rellenar el campo de texto", + "The new quantity should be smaller than the old one": "La nueva cantidad debe de ser menor que la anterior", + "The value should not be greater than 100%": "El valor no debe de ser mayor de 100%", + "The value should be a number": "El valor debe ser un numero", + "This order is not editable": "Esta orden no se puede modificar", + "You can't create an order for a frozen client": "No puedes crear una orden para un cliente congelado", + "You can't create an order for a client that has a debt": "No puedes crear una orden para un cliente con deuda", + "is not a valid date": "No es una fecha valida", + "Barcode must be unique": "El código de barras debe ser único", + "The warehouse can't be repeated": "El almacén no puede repetirse", + "The tag or priority can't be repeated for an item": "El tag o prioridad no puede repetirse para un item", + "The observation type can't be repeated": "El tipo de observación no puede repetirse", + "A claim with that sale already exists": "Ya existe una reclamación para esta línea", + "You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo", + "Warehouse cannot be blank": "El almacén no puede quedar en blanco", + "Agency cannot be blank": "La agencia no puede quedar en blanco", + "Not enough privileges to edit a client with verified data": "No tienes permisos para hacer cambios en un cliente con datos comprobados", + "This address doesn't exist": "Este consignatario no existe", + "You must delete the claim id %d first": "Antes debes borrar la reclamación %d", + "You don't have enough privileges": "No tienes suficientes permisos", + "Cannot check Equalization Tax in this NIF/CIF": "No se puede marcar RE en este NIF/CIF", + "You can't make changes on the basic data of an confirmed order or with rows": "No puedes cambiar los datos basicos de una orden con artículos", + "INVALID_USER_NAME": "El nombre de usuario solo debe contener letras minúsculas o, a partir del segundo carácter, números o subguiones, no esta permitido el uso de la letra ñ", + "You can't create a ticket for a frozen client": "No puedes crear un ticket para un cliente congelado", + "You can't create a ticket for a inactive client": "No puedes crear un ticket para un cliente inactivo", + "Tag value cannot be blank": "El valor del tag no puede quedar en blanco", + "ORDER_EMPTY": "Cesta vacía", + "You don't have enough privileges to do that": "No tienes permisos para cambiar esto", + "NO SE PUEDE DESACTIVAR EL CONSIGNAT": "NO SE PUEDE DESACTIVAR EL CONSIGNAT", + "Error. El NIF/CIF está repetido": "Error. El NIF/CIF está repetido", + "Street cannot be empty": "Dirección no puede estar en blanco", + "City cannot be empty": "Cuidad no puede estar en blanco", + "Code cannot be blank": "Código no puede estar en blanco", + "You cannot remove this department": "No puedes eliminar este departamento", + "The extension must be unique": "La extensión debe ser unica", + "The secret can't be blank": "La contraseña no puede estar en blanco", + "We weren't able to send this SMS": "No hemos podido enviar el SMS", + "This client can't be invoiced": "Este cliente no puede ser facturado", + "This ticket can't be invoiced": "Este ticket no puede ser facturado", + "You cannot add or modify services to an invoiced ticket": "No puedes añadir o modificar servicios a un ticket facturado", + "This ticket can not be modified": "Este ticket no puede ser modificado", + "The introduced hour already exists": "Esta hora ya ha sido introducida", + "INFINITE_LOOP": "Existe una dependencia entre dos Jefes", + "The sales of the current ticket can't be modified": "Las lineas de este ticket no pueden ser modificadas", + "The sales of the receiver ticket can't be modified": "Las lineas del ticket al que envias no pueden ser modificadas", + "NO_AGENCY_AVAILABLE": "No hay una zona de reparto disponible con estos parámetros", + "ERROR_PAST_SHIPMENT": "No puedes seleccionar una fecha de envío en pasado", + "The current ticket can't be modified": "El ticket actual no puede ser modificado", + "The current claim can't be modified": "La reclamación actual no puede ser modificada", + "The sales of this ticket can't be modified": "Las lineas de este ticket no pueden ser modificadas", + "Sale(s) blocked, contact production": "Linea(s) bloqueada(s), contacte con produccion", + "Please select at least one sale": "Por favor selecciona al menos una linea", + "All sales must belong to the same ticket": "Todas las lineas deben pertenecer al mismo ticket", + "NO_ZONE_FOR_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada", + "This item doesn't exists": "El artículo no existe", + "NOT_ZONE_WITH_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada", + "Extension format is invalid": "El formato de la extensión es inválido", + "Invalid parameters to create a new ticket": "Parámetros inválidos para crear un nuevo ticket", + "This item is not available": "Este artículo no está disponible", + "This postcode already exists": "Este código postal ya existe", + "Concept cannot be blank": "El concepto no puede quedar en blanco", + "File doesn't exists": "El archivo no existe", + "You don't have privileges to change the zone": "No tienes permisos para cambiar la zona o para esos parámetros hay más de una opción de envío, hable con las agencias", + "This ticket is already on weekly tickets": "Este ticket ya está en tickets programados", + "Ticket id cannot be blank": "El id de ticket no puede quedar en blanco", + "Weekday cannot be blank": "El día de la semana no puede quedar en blanco", + "You can't delete a confirmed order": "No puedes borrar un pedido confirmado", + "The social name has an invalid format": "El nombre fiscal tiene un formato incorrecto", + "Invalid quantity": "Cantidad invalida", + "This postal code is not valid": "This postal code is not valid", + "is invalid": "is invalid", + "The postcode doesn't exist. Please enter a correct one": "El código postal no existe. Por favor, introduce uno correcto", + "The department name can't be repeated": "El nombre del departamento no puede repetirse", + "This phone already exists": "Este teléfono ya existe", + "You cannot move a parent to its own sons": "No puedes mover un elemento padre a uno de sus hijos", + "You can't create a claim for a removed ticket": "No puedes crear una reclamación para un ticket eliminado", + "You cannot delete a ticket that part of it is being prepared": "No puedes eliminar un ticket en el que una parte que está siendo preparada", + "You must delete all the buy requests first": "Debes eliminar todas las peticiones de compra primero", + "You should specify a date": "Debes especificar una fecha", + "You should specify at least a start or end date": "Debes especificar al menos una fecha de inicio o de fín", + "Start date should be lower than end date": "La fecha de inicio debe ser menor que la fecha de fín", + "You should mark at least one week day": "Debes marcar al menos un día de la semana", + "Swift / BIC can't be empty": "Swift / BIC no puede estar vacío", + "Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios", + "Incoterms is required for a non UEE member": "El incoterms es requerido para los clientes extracomunitarios", + "Deleted sales from ticket": "He eliminado las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{deletions}}}", + "Added sale to ticket": "He añadido la siguiente linea al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}", + "Changed sale discount": "He cambiado el descuento de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Created claim": "He creado la reclamación [{{claimId}}]({{{claimUrl}}}) de las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Changed sale price": "He cambiado el precio de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* del ticket [{{ticketId}}]({{{ticketUrl}}})", + "Changed sale quantity": "He cambiado la cantidad de [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}* del ticket [{{ticketId}}]({{{ticketUrl}}})", + "State": "Estado", + "regular": "normal", + "reserved": "reservado", + "Changed sale reserved state": "He cambiado el estado reservado de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Bought units from buy request": "Se ha comprado {{quantity}} unidades de [{{itemId}} {{concept}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})", + "Deny buy request": "Se ha rechazado la petición de compra para el ticket id [{{ticketId}}]({{{url}}}). Motivo: {{observation}}", + "MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*", + "Changed client paymethod": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})", + "Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})", + "Claim will be picked": "Se recogerá el género de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}*", + "Claim state has changed to incomplete": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *incompleta*", + "Claim state has changed to canceled": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *anulado*", + "Client checked as validated despite of duplication": "Cliente comprobado a pesar de que existe el cliente id {{clientId}}", + "ORDER_ROW_UNAVAILABLE": "No hay disponibilidad de este producto", + "Distance must be lesser than 1000": "La distancia debe ser inferior a 1000", + "This ticket is deleted": "Este ticket está eliminado", + "Unable to clone this travel": "No ha sido posible clonar este travel", + "This thermograph id already exists": "La id del termógrafo ya existe", + "Choose a date range or days forward": "Selecciona un rango de fechas o días en adelante", + "ORDER_ALREADY_CONFIRMED": "ORDER_ALREADY_CONFIRMED", + "Invalid password": "Invalid password", + "Password does not meet requirements": "La contraseña no cumple los requisitos", + "Role already assigned": "Role already assigned", + "Invalid role name": "Invalid role name", + "Role name must be written in camelCase": "Role name must be written in camelCase", + "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 {{author}} ha añadido una ausencia de tipo '{{absenceType}}' a {{employee}} para el día {{dated}}.", + "Deleted absence": "El empleado {{author}} ha eliminado una ausencia de tipo '{{absenceType}}' a {{employee}} del día {{dated}}.", + "I have deleted the ticket id": "He eliminado el ticket id [{{id}}]({{{url}}})", + "I have restored the ticket id": "He restaurado el ticket id [{{id}}]({{{url}}})", + "You can only restore a ticket within the first hour after deletion": "Únicamente puedes restaurar el ticket dentro de la primera hora después de su eliminación", + "Changed this data from the ticket": "He cambiado estos datos del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "agencyModeFk": "Agencia", + "clientFk": "Cliente", + "zoneFk": "Zona", + "warehouseFk": "Almacén", + "shipped": "F. envío", + "landed": "F. entrega", + "addressFk": "Consignatario", + "companyFk": "Empresa", + "The social name cannot be empty": "La razón social no puede quedar en blanco", + "The nif cannot be empty": "El NIF no puede quedar en blanco", + "You need to fill sage information before you check verified data": "Debes rellenar la información de sage antes de marcar datos comprobados", + "ASSIGN_ZONE_FIRST": "Asigna una zona primero", + "Amount cannot be zero": "El importe no puede ser cero", + "Company has to be official": "Empresa inválida", + "You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria", + "Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas", + "The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta", + "Sorts whole route": "Reordena ruta entera", + "New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*", + "New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}*", + "Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío", + "This BIC already exist.": "Este BIC ya existe.", + "That item doesn't exists": "Ese artículo no existe", + "There's a new urgent ticket:": "Hay un nuevo ticket urgente:", + "Invalid account": "Cuenta inválida", + "Compensation account is empty": "La cuenta para compensar está vacia", + "This genus already exist": "Este genus ya existe", + "This specie already exist": "Esta especie ya existe", + "Client assignment has changed": "He cambiado el comercial ~*\"<{{previousWorkerName}}>\"*~ por *\"<{{currentWorkerName}}>\"* del cliente [{{clientName}} ({{clientId}})]({{{url}}})", + "None": "Ninguno", + "The contract was not active during the selected date": "El contrato no estaba activo durante la fecha seleccionada", + "Cannot add more than one '1/2 day vacation'": "No puedes añadir más de un 'Vacaciones 1/2 dia'", + "This document already exists on this ticket": "Este documento ya existe en el ticket", + "Some of the selected tickets are not billable": "Algunos de los tickets seleccionados no son facturables", + "You can't invoice tickets from multiple clients": "No puedes facturar tickets de multiples clientes", + "nickname": "nickname", + "INACTIVE_PROVIDER": "Proveedor inactivo", + "This client is not invoiceable": "Este cliente no es facturable", + "serial non editable": "Esta serie no permite asignar la referencia", + "Max shipped required": "La fecha límite es requerida", + "Can't invoice to future": "No se puede facturar a futuro", + "Can't invoice to past": "No se puede facturar a pasado", + "This ticket is already invoiced": "Este ticket ya está facturado", + "A ticket with an amount of zero can't be invoiced": "No se puede facturar un ticket con importe cero", + "A ticket with a negative base can't be invoiced": "No se puede facturar un ticket con una base negativa", + "Global invoicing failed": "[Facturación global] No se han podido facturar algunos clientes", + "Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes", + "Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio", + "You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito", + "You can't change the credit set to zero from a financialBoss": "No puedes cambiar el cŕedito establecido a cero por un jefe de finanzas", + "Amounts do not match": "Las cantidades no coinciden", + "The PDF document does not exists": "El documento PDF no existe. Prueba a regenerarlo desde la opción 'Regenerar PDF factura'", + "The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos", + "You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días", + "The worker has hours recorded that day": "El trabajador tiene horas fichadas ese día", + "The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día", + "You can not modify is pay method checked": "No se puede modificar el campo método de pago validado", + "Can't transfer claimed sales": "No puedes transferir lineas reclamadas", + "You don't have privileges to create refund": "No tienes permisos para crear un abono", + "The item is required": "El artículo es requerido", + "The agency is already assigned to another autonomous": "La agencia ya está asignada a otro autónomo", + "date in the future": "Fecha en el futuro", + "reference duplicated": "Referencia duplicada", + "This ticket is already a refund": "Este ticket ya es un abono", + "isWithoutNegatives": "isWithoutNegatives", + "routeFk": "routeFk", + "Can't change the password of another worker": "No se puede cambiar la contraseña de otro trabajador", + "No hay un contrato en vigor": "No hay un contrato en vigor", + "No se permite fichar a futuro": "No se permite fichar a futuro", + "No está permitido trabajar": "No está permitido trabajar", + "Fichadas impares": "Fichadas impares", + "Descanso diario 12h.": "Descanso diario 12h.", + "Descanso semanal 36h. / 72h.": "Descanso semanal 36h. / 72h.", + "Dirección incorrecta": "Dirección incorrecta", + "Modifiable user details only by an administrator": "Detalles de usuario modificables solo por un administrador", + "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", + "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" } \ No newline at end of file From 1bc8cf52d5294fc5e2670df30231c132aaf6fad1 Mon Sep 17 00:00:00 2001 From: vicent Date: Tue, 8 Nov 2022 08:20:36 +0100 Subject: [PATCH 17/21] add translate --- loopback/locale/es.json | 1 + 1 file changed, 1 insertion(+) diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 1805682af3..8f29a31827 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -237,6 +237,7 @@ "Modifiable user details only by an administrator": "Detalles de usuario modificables solo por un administrador", "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", From 11459bbd5665300fe0d232e3eadc6b92dfa3b7f4 Mon Sep 17 00:00:00 2001 From: vicent Date: Tue, 8 Nov 2022 08:46:36 +0100 Subject: [PATCH 18/21] refactor: change column name --- db/changes/10502-november/00-workerTimeControlMail.sql | 1 + .../worker-time-control/updateWorkerTimeControlMail.js | 10 +++++----- .../worker/back/models/worker-time-control-mail.json | 2 +- modules/worker/front/time-control/index.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 db/changes/10502-november/00-workerTimeControlMail.sql diff --git a/db/changes/10502-november/00-workerTimeControlMail.sql b/db/changes/10502-november/00-workerTimeControlMail.sql new file mode 100644 index 0000000000..e3d169a837 --- /dev/null +++ b/db/changes/10502-november/00-workerTimeControlMail.sql @@ -0,0 +1 @@ +ALTER TABLE `vn`.`workerTimeControlMail` CHANGE emailResponse reason text CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL NULL; \ No newline at end of file diff --git a/modules/worker/back/methods/worker-time-control/updateWorkerTimeControlMail.js b/modules/worker/back/methods/worker-time-control/updateWorkerTimeControlMail.js index 3bb2b90136..a8dc14bb1e 100644 --- a/modules/worker/back/methods/worker-time-control/updateWorkerTimeControlMail.js +++ b/modules/worker/back/methods/worker-time-control/updateWorkerTimeControlMail.js @@ -24,7 +24,7 @@ module.exports = Self => { required: true }, { - arg: 'emailResponse', + arg: 'reason', type: 'string' }], returns: { @@ -58,13 +58,13 @@ module.exports = Self => { if (!workerTimeControlMail) throw new UserError(`There aren't records for this week`); const oldState = workerTimeControlMail.state; - const oldEmailResponse = workerTimeControlMail.emailResponse; + const oldReason = workerTimeControlMail.reason; if (oldState == args.state) throw new UserError('Already has this status'); await workerTimeControlMail.updateAttributes({ state: args.state, - emailResponse: args.emailResponse || null + reason: args.reason || null }, myOptions); const logRecord = { @@ -74,11 +74,11 @@ module.exports = Self => { changedModel: 'WorkerTimeControlMail', oldInstance: { state: oldState, - emailResponse: oldEmailResponse + reason: oldReason }, newInstance: { state: args.state, - emailResponse: args.emailResponse + reason: args.reason } }; diff --git a/modules/worker/back/models/worker-time-control-mail.json b/modules/worker/back/models/worker-time-control-mail.json index 0a0c2bf9a7..78b99881dc 100644 --- a/modules/worker/back/models/worker-time-control-mail.json +++ b/modules/worker/back/models/worker-time-control-mail.json @@ -26,7 +26,7 @@ "updated": { "type": "date" }, - "emailResponse": { + "reason": { "type": "string" } }, diff --git a/modules/worker/front/time-control/index.js b/modules/worker/front/time-control/index.js index 9bd9bb5717..c3d3e5eabd 100644 --- a/modules/worker/front/time-control/index.js +++ b/modules/worker/front/time-control/index.js @@ -322,7 +322,7 @@ class Controller extends Section { year: this.date.getFullYear(), week: weekNumber, state: 'REVISE', - emailResponse: this.reason + reason: this.reason }; const query = `WorkerTimeControls/updateWorkerTimeControlMail`; this.$http.post(query, params).then(() => { From 8d7296cc8f21c7e18d7f73a082a2d8b2b418dfaf Mon Sep 17 00:00:00 2001 From: vicent Date: Tue, 8 Nov 2022 08:46:47 +0100 Subject: [PATCH 19/21] feat: add backTest --- .../specs/sendMail.spec.js | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) 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 c9cbb5da88..dfb6855ca1 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 @@ -1,6 +1,6 @@ const models = require('vn-loopback/server/server').models; -describe('workerTimeControl sendMail()', () => { +fdescribe('workerTimeControl sendMail()', () => { const workerId = 18; const ctx = { req: { @@ -17,7 +17,7 @@ describe('workerTimeControl sendMail()', () => { jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; }); - it('should fill time control of a worker without records in Journey and and with rest', async() => { + it('should fill time control of a worker without records in Journey and with rest', async() => { const tx = await models.WorkerTimeControl.beginTransaction({}); try { @@ -41,7 +41,7 @@ describe('workerTimeControl sendMail()', () => { } }); - it('should fill time control of a worker without records in Journey and and without rest', async() => { + 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({}); @@ -68,7 +68,7 @@ describe('workerTimeControl sendMail()', () => { } }); - it('should fill time control of a worker with records in Journey', async() => { + it('should fill time control of a worker with records in Journey and with rest', async() => { const tx = await models.WorkerTimeControl.beginTransaction({}); try { @@ -76,8 +76,7 @@ describe('workerTimeControl sendMail()', () => { 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', ?), - (3, 2, '12:30:00', '19: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); @@ -100,6 +99,32 @@ describe('workerTimeControl sendMail()', () => { } }); + 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; }); From 4d81991d8607ba269333aadc66bc1d9b2b7fbb44 Mon Sep 17 00:00:00 2001 From: vicent Date: Tue, 8 Nov 2022 08:47:08 +0100 Subject: [PATCH 20/21] delete focus test --- .../back/methods/worker-time-control/specs/sendMail.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 dfb6855ca1..d0afd45b9b 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 @@ -1,6 +1,6 @@ const models = require('vn-loopback/server/server').models; -fdescribe('workerTimeControl sendMail()', () => { +describe('workerTimeControl sendMail()', () => { const workerId = 18; const ctx = { req: { From 163f7fd28420e02619c8e4b6c5dc0f18294338d4 Mon Sep 17 00:00:00 2001 From: joan Date: Fri, 11 Nov 2022 08:31:45 +0100 Subject: [PATCH 21/21] Added version 10503 --- db/changes/10503-november/delete.keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 db/changes/10503-november/delete.keep diff --git a/db/changes/10503-november/delete.keep b/db/changes/10503-november/delete.keep new file mode 100644 index 0000000000..e69de29bb2