From 40de826da9c46cdc1464e8e3814fbb258140a584 Mon Sep 17 00:00:00 2001 From: Muhammad Afif Date: Fri, 5 Jun 2026 09:17:45 +0700 Subject: [PATCH] add: reminder whatsapp sparepart monthly --- db/device.db.js | 17 ++- db/notification_error.db.js | 18 +++ db/sparepart.db.js | 21 ++- routes/notifikasi-wa.route.js | 10 ++ services/notification_error.service.js | 17 ++- services/notifikasi-wa.service.js | 179 ++++++++++++++++++++++++- 6 files changed, 249 insertions(+), 13 deletions(-) diff --git a/db/device.db.js b/db/device.db.js index b2b9968..390e80f 100644 --- a/db/device.db.js +++ b/db/device.db.js @@ -139,11 +139,26 @@ const getDeviceReminderChanelDb = async (id) => { return result.recordset; }; +const getDeviceReminderMonthlyDb = async (id) => { + const queryText = ` + SELECT + a.*, + b.brand_name, + COALESCE(a.device_code, '') + ' - ' + COALESCE(a.device_name, '') AS device_code_name + FROM m_device a + LEFT JOIN m_brands b ON a.brand_id = b.brand_id + WHERE a.device_id = $1 AND a.deleted_at IS NULL AND a.reminder_at_monthly IS NOT NULL + `; + const result = await pool.query(queryText, [id]); + return result.recordset; +}; + module.exports = { getAllDevicesDb, getDeviceByIdDb, createDeviceDb, updateDeviceDb, deleteDeviceDb, - getDeviceReminderChanelDb + getDeviceReminderChanelDb, + getDeviceReminderMonthlyDb }; diff --git a/db/notification_error.db.js b/db/notification_error.db.js index 3e7505f..3c52b5f 100644 --- a/db/notification_error.db.js +++ b/db/notification_error.db.js @@ -255,6 +255,23 @@ const getReminderNotificationErrorByYearlyDb = async (errorCode, chanel, year) = return result.recordset[0]; }; +const getReminderNotificationErrorByMonthlyDb = async (errorCode, monthly) => { + const queryText = ` + SELECT a.* + + FROM notification_error a + + WHERE a.error_code_id = $1 + AND MONTH(a.created_at) = $2 + AND a.message_error_issue LIKE 'reminder%' + AND a.is_active = 1 + AND a.deleted_at IS NULL + `; + + const result = await pool.query(queryText, [errorCode, monthly]); + return result.recordset[0]; +}; + module.exports = { getNotificationByIdDb, getDeviceNotificationByIdDb, @@ -264,6 +281,7 @@ module.exports = { getUsersNotificationErrorDb, getDeviceChannelReminder, getReminderNotificationErrorByYearlyDb, + getReminderNotificationErrorByMonthlyDb, updateNotificationErrorByChanelReminderDb }; diff --git a/db/sparepart.db.js b/db/sparepart.db.js index d6d50f0..f27b038 100644 --- a/db/sparepart.db.js +++ b/db/sparepart.db.js @@ -197,6 +197,24 @@ const getSparepartsByYearlyDb = async (yearly) => { return result.recordset; }; +const getSparepartsByMonthlyDb = async (monthly) => { + + const queryText = ` + SELECT * + FROM m_sparepart + WHERE deleted_at IS NULL + AND EXISTS ( + SELECT 1 + FROM OPENJSON(sparepart_monthly) + WHERE value = $1 + ) + `; + + const result = await pool.query(queryText, [monthly]); + + return result.recordset; +}; + module.exports = { getAllSparepartDb, getSparepartByIdDb, @@ -205,5 +223,6 @@ module.exports = { createSparepartDb, updateSparepartDb, deleteSparepartDb, - getSparepartsByYearlyDb + getSparepartsByYearlyDb, + getSparepartsByMonthlyDb }; diff --git a/routes/notifikasi-wa.route.js b/routes/notifikasi-wa.route.js index 8fd7b3a..3c7ce46 100644 --- a/routes/notifikasi-wa.route.js +++ b/routes/notifikasi-wa.route.js @@ -11,4 +11,14 @@ router.post('/restart-wa', async (req, res) => { } }); +router.post('/reminder-sparepart/:id', async (req, res) => { + try { + const { id } = req.params; + const result = await NotifikasiWaService.onMonthlySparepartReminder(id); + return res.status(200).json(result); + } catch (error) { + return res.status(500).json(error); + } +}); + module.exports = router; \ No newline at end of file diff --git a/services/notification_error.service.js b/services/notification_error.service.js index 03f423a..638ab42 100644 --- a/services/notification_error.service.js +++ b/services/notification_error.service.js @@ -31,7 +31,7 @@ const { } = require("../db/notification_wa.db"); const { ErrorHandler } = require("../helpers/error"); -const { getSparepartsByYearlyDb } = require("../db/sparepart.db"); +const { getSparepartsByYearlyDb, getSparepartsByMonthlyDb } = require("../db/sparepart.db"); const notifikasiWaService = require("./notifikasi-wa.service"); @@ -140,11 +140,18 @@ class NotificationService { notification.is_reminder = false; notification.spareparts_reminder = []; if (notification.message_error_issue) { - const sparepartsReminder = await getSparepartsByYearlyDb(notification.error_code_id); - notification.spareparts_reminder = sparepartsReminder ?? []; + const [sparepartsYearly, sparepartsMonthly] = await Promise.all([ + getSparepartsByYearlyDb(notification.error_code_id), + getSparepartsByMonthlyDb(notification.error_code_id) + ]); + + notification.spareparts_reminder = [ + ...(sparepartsYearly ?? []), + ...(sparepartsMonthly ?? []) + ]; + notification.is_reminder = true; - } return notification; @@ -172,7 +179,7 @@ class NotificationService { if (!notification.is_read) { const updateStatus = await updateNotificationErrorDb( notification_error_id, - { is_read: true} + { is_read: true } ); if (!updateStatus) { diff --git a/services/notifikasi-wa.service.js b/services/notifikasi-wa.service.js index 6f567c4..35f1828 100644 --- a/services/notifikasi-wa.service.js +++ b/services/notifikasi-wa.service.js @@ -3,6 +3,8 @@ const { InsertNotificationErrorDb, updateNotificationErrorDb, getReminderNotificationErrorByYearlyDb, + getDeviceNotificationByIdDb, + getReminderNotificationErrorByMonthlyDb, updateNotificationErrorByChanelReminderDb, } = require("../db/notification_error.db"); const { @@ -16,15 +18,14 @@ const { sendNotifikasi, } = require("../db/notification_wa.db"); const { getErrorCodeByBrandAndCodeDb } = require("../db/brand_code.db"); -const { getDeviceNotificationByIdDb } = require("../db/notification_error.db"); const { exec } = require("child_process"); const util = require("util"); const execPromise = util.promisify(exec); const fs = require('fs').promises; const path = require("path"); -const { getDeviceReminderChanelDb } = require("../db/device.db"); - +const { getDeviceReminderChanelDb, getDeviceReminderMonthlyDb } = require("../db/device.db"); +const { getSparepartsByMonthlyDb } = require("../db/sparepart.db"); const baseDir = path.resolve(__dirname, '../scheduler'); const filePath = path.join(baseDir, 'reminder.json'); // const filePathLog = path.join(baseDir, 'log.json'); @@ -497,7 +498,7 @@ class NotifikasiWaService { let isSendNotification = false; for (const dataUser of dataUsers) { - if (dataUser.is_active) { + if (dataUser.is_active || dataUser.is_active === 1) { const tokenRedirect = await generateTokenRedirect( dataUser.contact_phone, @@ -513,8 +514,7 @@ class NotifikasiWaService { `Hai ${dataUser.contact_name || "-"},\n` + `Diberitahukan bahwa terdapat sparepart pada device "${deviceName ?? "-"}" ` + `yang telah memasuki jadwal perawatan tahunan.\n` + - `\nSilakan segera lakukan pengecekan dan perawatan untuk memastikan kinerja tetap optimal.` + - `\nDetail sparepart dapat dilihat pada link berikut:\n${shortUrl}`; + `\nSilakan segera lakukan pengecekan dan perawatan untuk memastikan kinerja tetap optimal.`; const param = { idData: resultNotificationError.notification_error_id, @@ -591,6 +591,173 @@ class NotifikasiWaService { } } + async onMonthlySparepartReminder(deviceId) { + try { + const paramDb = { + limit: 100, + page: 1, + criteria: "", + active: 1, + }; + + const allDeviceReminders = await getDeviceReminderMonthlyDb(deviceId); + + if (!allDeviceReminders || allDeviceReminders.length === 0) { + return { success: false, message: 'Tidak ada data device.' }; + } + + const now = dayjs().tz(timeZone); + const currentYear = dayjs().tz(timeZone).year(); + const currentMonth = dayjs().tz(timeZone).month() + 1; + + for (const deviceReminder of allDeviceReminders) { + + if (deviceReminder?.reminder_at_monthly) { + const deviceName = deviceReminder.device_name ?? '-'; + + const reminderAt = dayjs.tz( + deviceReminder.reminder_at_monthly, + timeZone + ); + + const nowKey = now.format('MM-DD HH:mm'); + + const reminderKey = reminderAt.format('MM-DD HH:mm'); + + const isTriggered = nowKey >= reminderKey; + + if (isTriggered) { + const monthlyCode = parseInt(`${currentMonth}`); + + const checkNotifExist = await getReminderNotificationErrorByMonthlyDb( + monthlyCode, + currentMonth + ); + + if (!checkNotifExist) { + + const spareparts = await getSparepartsByMonthlyDb(currentMonth); + + let sparepartList = ""; + if (spareparts && spareparts.length > 0) { + sparepartList = "\n\nDaftar sparepart yang perlu diperiksa:\n" + + spareparts.map((sp, idx) => `${idx + 1}. ${sp.sparepart_name || 'Sparepart'}`).join('\n'); + } + + const data = { + error_code_id: monthlyCode, + error_chanel: 0, + is_send: 0, + is_delivered: 0, + is_read: 0, + is_active: 1, + message_error_issue: `reminder device ${deviceName} in month ${currentMonth}`, + }; + + const resultNotificationError = await InsertNotificationErrorDb(data); + + const results = await getAllContactDb(paramDb); + + const dataUsers = results.data; + + let isSendNotification = false; + + for (const dataUser of dataUsers) { + if (dataUser.is_active || dataUser.is_active === 1) { + + return { success: false, message: 'Tidak ada data user yang aktif.' }; + + const tokenRedirect = await generateTokenRedirect( + dataUser.contact_phone, + dataUser.contact_name, + resultNotificationError.notification_error_id + ); + + const encodedToken = encodeURIComponent(tokenRedirect); + + const shortUrl = await shortUrltiny(encodedToken); + + const bodyMessage = + `Hai ${dataUser.contact_name || "-"},\n\n` + + `Diberitahukan bahwa terdapat sparepart pada device "${deviceName}" ` + + `yang telah memasuki jadwal perawatan bulanan untuk bulan ${currentMonth} pada tahun ${currentYear}.${sparepartList}\n\n` + + `Silakan segera lakukan pengecekan dan perawatan untuk memastikan kinerja tetap optimal.`; + + const param = { + idData: resultNotificationError.notification_error_id, + userPhone: dataUser.contact_phone, + userName: dataUser.contact_name, + bodyMessage: bodyMessage, + }; + + const resultNotificationErrorUser = + await createNotificationErrorUserDb({ + notification_error_id: param.idData, + + contact_phone: param.userPhone, + contact_name: param.userName, + message_error_issue: param.bodyMessage, + is_send: false, + }); + + const resultSend = await sendNotifikasi( + param.userPhone, + param.bodyMessage + ); + + // await this.saveLogReminder({ + // message: `Reminder dijalankan`, + // resultSend + // }) + + await updateNotificationErrorUserDb( + resultNotificationErrorUser[0].notification_error_user_id, + { + is_send: resultSend.success, + } + ); + + await this.saveReminder({ + notification_log: resultNotificationError.notification_error_id, + error_code_id: data['error_code_id'], + error_chanel: data['error_chanel'], + start_at: reminderAt.format('HH:mm'), + interval: 1, + max: 3, + active: 1, + count: 0, + last_run: now.toISOString(), + last_run_indo: now.format('DD-MM-YYYY HH:mm:ss'), + next_run: null, + next_run_indo: null, + message: `Reminder untuk ${deviceName} dengan reminder bulan ${currentMonth} telah dijalankan pada ${now.format('DD-MM-YYYY HH:mm:ss')}, hasil pengiriman: ${resultSend.success ? 'sukses' : 'gagal'}`, + }, false); + + if (resultSend.success) { + isSendNotification = resultSend.success; + } + } + } + + await updateNotificationErrorDb( + resultNotificationError.notification_error_id, + { + is_send: isSendNotification, + is_delivered: isSendNotification, + } + ); + } + } + } + } + + } catch (err) { + console.log('Error onNotificationReminder:', err); + throw err; + } + } + + async restartWhatsapp() { try {