From 786ba0d614995377d769e54a4f9897a60e6a10a2 Mon Sep 17 00:00:00 2001 From: vicent Date: Mon, 31 Oct 2022 07:34:34 +0100 Subject: [PATCH] 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 5b769e285..3d048f2cc 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 000000000..970185951 --- /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 c155e331d..bca24663b 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 000000000..b7d5f2817 --- /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 000000000..df9257540 --- /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 45f4e2194..928606b7d 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 ab07802ca..bc3e53501 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 170d88b21..1b60dcce0 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 8b2486f05..cace38291 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