From 61aad8ea4ac48f822844f9c7889a823b97aba4b2 Mon Sep 17 00:00:00 2001 From: Rizki Date: Fri, 10 Apr 2026 14:08:21 +0700 Subject: [PATCH] feat: send email when need sign justification --- adapter/justificationadapter.js | 11 ++- emailnotif.js | 121 ++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 emailnotif.js diff --git a/adapter/justificationadapter.js b/adapter/justificationadapter.js index f39a5d5..8abd80d 100644 --- a/adapter/justificationadapter.js +++ b/adapter/justificationadapter.js @@ -1,6 +1,7 @@ const db=require('../config/dbproc.js'); // const slashes = require('slashes'); const QRCode = require('qrcode'); +const emailNotif = require('../emailnotif.js'); const Adapter=require('./dbadapter.js'); const fs = require("fs"); const { callbackPromise } = require('nodemailer/lib/shared/index.js'); @@ -386,9 +387,10 @@ class JustificationAdapter extends Adapter{ // } // else{ // apires.meta.code = 200; - // apires.meta.message = "Record Not Found"; + // apires.meta.message = "Record Not Found"; // } callback(null, apires); + emailNotif.notifyOnSubmit(idxjustification); } }); } catch (err) { @@ -397,7 +399,7 @@ class JustificationAdapter extends Adapter{ callback('error',apires); } } - + async queryConvertdocxtoPdf(req, callback) { try { var apires = this.getApiResultDefined(); @@ -557,7 +559,10 @@ class JustificationAdapter extends Adapter{ "token":token, "urltoken": process.env.BASEURL+"/main/getimageinfo?route=png&name="+filepng }; - callback(null, apires); + callback(null, apires); + if (!isapproved) { + emailNotif.notifyOnSigned(idxjustification, nikapproval); + } } }); diff --git a/emailnotif.js b/emailnotif.js new file mode 100644 index 0000000..5057804 --- /dev/null +++ b/emailnotif.js @@ -0,0 +1,121 @@ +const http = require('http'); +const db = require('./config/dbproc.js'); + +const RECIPIENT = 'rizki.rmdhn1304@gmail.com'; + +function buildHtml(approverName, title, priorName) { + return `

Dear ${approverName},

+

A new workflow task requires your review and approval. Please find the details regarding this justification below:

+ +

Action Required:
+https://e-portal.telkomcel.tl/app/ext/eproc/

+

Best regards,
E-Procurement

`; +} + +function postEmail(approverName, title, priorName) { + const payload = JSON.stringify({ + to: [RECIPIENT], + subject: `[Approval Required] Justification for ${title}`, + html: buildHtml(approverName, title, priorName) + }); + + const req = http.request({ + hostname: 'localhost', + port: 7004, + path: '/api/email/send', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(payload) + } + }, (res) => { + console.log('[email-notif] sent, status:', res.statusCode); + }); + + req.on('error', (err) => { + console.error('[email-notif] failed:', err.message); + }); + + req.write(payload); + req.end(); +} + +/** + * Notify first approver when justification is submitted. + */ +function notifyOnSubmit(idxjustification) { + const qryJustification = ` + select j.title, e.fullname as creator_name + from tbl_justification j + join dbssotcel.tbl_employee e on e.nik = j.iby + where j._idx = '${idxjustification}' limit 1 + `; + db.query(qryJustification, [], function(err, rows) { + if (err || rows.length === 0) { + console.error('[email-notif] notifyOnSubmit: failed to get justification data', err); + return; + } + const title = rows[0].title; + const creatorName = rows[0].creator_name; + + const qryApprover = ` + select t.nik, e.fullname + from tbl_justificationttd t + join dbssotcel.tbl_employee e on e.nik = t.nik + where t.idxjustification = '${idxjustification}' and t.category = 'APR' and t.issigned = 0 and t.isdeleted = 0 + order by t._idx asc limit 1 + `; + db.query(qryApprover, [], function(err2, rows2) { + if (err2 || rows2.length === 0) { + console.error('[email-notif] notifyOnSubmit: no approver found', err2); + return; + } + postEmail(rows2[0].fullname, title, creatorName); + }); + }); +} + +/** + * Notify next approver after someone signs. + */ +function notifyOnSigned(idxjustification, signerNik) { + const qryTitle = `select title from tbl_justification where _idx = '${idxjustification}' limit 1`; + db.query(qryTitle, [], function(err, rows) { + if (err || rows.length === 0) { + console.error('[email-notif] notifyOnSigned: failed to get justification title', err); + return; + } + const title = rows[0].title; + + const qrySigner = `select fullname from dbssotcel.tbl_employee where nik = '${signerNik}' limit 1`; + db.query(qrySigner, [], function(err2, rows2) { + if (err2 || rows2.length === 0) { + console.error('[email-notif] notifyOnSigned: failed to get signer name', err2); + return; + } + const signerName = rows2[0].fullname; + + const qryNext = ` + select t.nik, e.fullname + from tbl_justificationttd t + join dbssotcel.tbl_employee e on e.nik = t.nik + where t.idxjustification = '${idxjustification}' and t.category = 'APR' and t.issigned = 0 and t.isdeleted = 0 + order by t._idx asc limit 1 + `; + db.query(qryNext, [], function(err3, rows3) { + if (err3 || rows3.length === 0) { + console.log('[email-notif] notifyOnSigned: no more approvers to notify'); + return; + } + postEmail(rows3[0].fullname, title, signerName); + }); + }); + }); +} + +module.exports = { notifyOnSubmit, notifyOnSigned };