feat: send email when need sign justification

This commit is contained in:
Rizki
2026-04-10 14:08:21 +07:00
parent a3d5974a21
commit 61aad8ea4a
2 changed files with 129 additions and 3 deletions

View File

@ -1,6 +1,7 @@
const db=require('../config/dbproc.js'); const db=require('../config/dbproc.js');
// const slashes = require('slashes'); // const slashes = require('slashes');
const QRCode = require('qrcode'); const QRCode = require('qrcode');
const emailNotif = require('../emailnotif.js');
const Adapter=require('./dbadapter.js'); const Adapter=require('./dbadapter.js');
const fs = require("fs"); const fs = require("fs");
const { callbackPromise } = require('nodemailer/lib/shared/index.js'); const { callbackPromise } = require('nodemailer/lib/shared/index.js');
@ -386,9 +387,10 @@ class JustificationAdapter extends Adapter{
// } // }
// else{ // else{
// apires.meta.code = 200; // apires.meta.code = 200;
// apires.meta.message = "Record Not Found"; // apires.meta.message = "Record Not Found";
// } // }
callback(null, apires); callback(null, apires);
emailNotif.notifyOnSubmit(idxjustification);
} }
}); });
} catch (err) { } catch (err) {
@ -397,7 +399,7 @@ class JustificationAdapter extends Adapter{
callback('error',apires); callback('error',apires);
} }
} }
async queryConvertdocxtoPdf(req, callback) { async queryConvertdocxtoPdf(req, callback) {
try { try {
var apires = this.getApiResultDefined(); var apires = this.getApiResultDefined();
@ -557,7 +559,10 @@ class JustificationAdapter extends Adapter{
"token":token, "token":token,
"urltoken": process.env.BASEURL+"/main/getimageinfo?route=png&name="+filepng "urltoken": process.env.BASEURL+"/main/getimageinfo?route=png&name="+filepng
}; };
callback(null, apires); callback(null, apires);
if (!isapproved) {
emailNotif.notifyOnSigned(idxjustification, nikapproval);
}
} }
}); });

121
emailnotif.js Normal file
View File

@ -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 `<p>Dear ${approverName},</p>
<p>A new workflow task requires your review and approval. Please find the details regarding this justification below:</p>
<ul>
<li><strong>Justification:</strong> ${title}</li>
<li><strong>Task Type:</strong> OPEN</li>
<li><strong>Status:</strong> READY FOR APPROVAL</li>
<li><strong>Sent By:</strong> ${priorName}</li>
</ul>
<p><strong>Action Required:</strong><br>
<a href="https://e-portal.telkomcel.tl/app/ext/eproc/">https://e-portal.telkomcel.tl/app/ext/eproc/</a></p>
<p>Best regards,<br>E-Procurement</p>`;
}
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 };