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 = {userId: ctx.req.accessToken.userId}; 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('DROP TEMPORARY TABLE IF EXISTS tmp.`user`'); stmts.push(stmt); stmt = new ParameterizedSQL('CREATE TEMPORARY TABLE tmp.`user` SELECT id userFk FROM account.user WHERE id = ?', [args.workerId]); 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('DROP TEMPORARY TABLE IF EXISTS tmp.`user`'); stmts.push(stmt); stmt = new ParameterizedSQL('CREATE TEMPORARY TABLE IF NOT EXISTS tmp.`user` SELECT userFk FROM vn.worker w JOIN account.`user` u ON u.id = w.userFk WHERE userFk IS NOT NULL'); stmts.push(stmt); } stmt = new ParameterizedSQL( `CALL vn.timeControl_calculate(?, ?) `, [started, ended]); stmts.push(stmt); stmt = new ParameterizedSQL( `CALL vn.timeBusiness_calculate(?, ?) `, [started, ended]); stmts.push(stmt); stmt = new ParameterizedSQL( `CALL vn.timeControl_getError(?, ?) `, [started, ended]); stmts.push(stmt); stmt = new ParameterizedSQL(` INSERT INTO mail (receiver, subject, body) SELECT CONCAT(u.name, '@verdnatura.es'), CONCAT('Error registro de horas semana ', ?, ' año ', ?) , CONCAT('No se ha podido enviar el registro de horas al empleado/s: ', GROUP_CONCAT(DISTINCT CONCAT('
', w.id, ' ', w.firstName, ' ', w.lastName))) FROM tmp.timeControlError tce JOIN vn.workerTimeControl wtc ON wtc.id = tce.id JOIN worker w ON w.id = wtc.userFK JOIN account.user u ON u.id = w.bossFk GROUP BY w.bossFk `, [args.week, args.year]); 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 LEFT JOIN ( SELECT DISTINCT wtc.userFk FROM tmp.timeControlError tce JOIN vn.workerTimeControl wtc ON wtc.id = tce.id )sub ON sub.userFk = tb.userFk WHERE sub.userFK IS NULL AND IFNULL(?, u.id) = u.id AND b.companyCodeFk = 'VNL' AND w.businessFk AND d.isTeleworking 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.BusinessSchedule.find({ where: { businessFk: day.businessFk, weekday: weekDay } }, myOptions); let timeTableDecimalInSeconds = 0; for (let journey of journeys) { const start = Date.vnNew(); const [startHours, startMinutes, startSeconds] = getTime(journey.started); start.setHours(startHours, startMinutes, startSeconds, 0); const end = Date.vnNew(); const [endHours, endMinutes, endSeconds] = getTime(journey.ended); 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.started); await models.WorkerTimeControl.create({ userFk: day.workerFk, timed: timed.setHours(startHours, startMinutes, startSeconds), manual: true, isSendMail: true }, myOptions); const [endHours, endMinutes, endSeconds] = getTime(journey.ended); 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.started); 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.started); 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) { const query = `INSERT IGNORE INTO workerTimeControlMail (workerFk, year, week) VALUES(?, ?, ?);`; await Self.rawSql(query, [previousWorkerFk, args.year, args.week]); ctx.args = { recipient: previousReceiver, year: args.year, week: args.week, workerId: previousWorkerFk, state: 'SENDED' }; await models.WorkerTimeControl.weeklyHourRecordEmail(ctx, myOptions); previousWorkerFk = day.workerFk; previousReceiver = day.receiver; } if (tx) { await tx.commit(); delete myOptions.transaction; } } catch (e) { const stmts = []; let stmt; stmt = new ParameterizedSQL(` INSERT INTO mail (receiver, subject, body) SELECT CONCAT(u.name, '@verdnatura.es'), CONCAT('Error registro de horas semana ', ?, ' año ', ?) , CONCAT('No se ha podido enviar el registro de horas al empleado: ', w.id, ' ', w.firstName, ' ', w.lastName, ' por el motivo: ', ?) FROM worker w JOIN account.user u ON u.id = w.bossFk WHERE w.id = ? `, [args.week, args.year, e.message, day.workerFk]); stmts.push(stmt); const sql = ParameterizedSQL.join(stmts, ';'); await conn.executeStmt(sql); previousWorkerFk = day.workerFk; previousReceiver = day.receiver; if (tx) await tx.rollback(); delete myOptions.transaction; continue; } } 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)]; } };