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