WIP: Criado Watchdog/Goleito para detectar chamados que estao fechados no hubsoft mas nao no GLPI, testes pendentes
This commit is contained in:
parent
b2b7364418
commit
1bf3f744ab
10
package-lock.json
generated
10
package-lock.json
generated
@ -15,6 +15,7 @@
|
|||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"mysql2": "^3.15.2",
|
"mysql2": "^3.15.2",
|
||||||
"node-cron": "^4.2.1",
|
"node-cron": "^4.2.1",
|
||||||
|
"nodemailer": "^7.0.12",
|
||||||
"pg": "^8.16.3",
|
"pg": "^8.16.3",
|
||||||
"pm2": "^6.0.13",
|
"pm2": "^6.0.13",
|
||||||
"qs": "^6.14.0",
|
"qs": "^6.14.0",
|
||||||
@ -1890,6 +1891,15 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nodemailer": {
|
||||||
|
"version": "7.0.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.12.tgz",
|
||||||
|
"integrity": "sha512-H+rnK5bX2Pi/6ms3sN4/jRQvYSMltV6vqup/0SFOrxYYY/qoNvhXPlYq3e+Pm9RFJRwrMGbMIwi81M4dxpomhA==",
|
||||||
|
"license": "MIT-0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nodemon": {
|
"node_modules/nodemon": {
|
||||||
"version": "3.1.11",
|
"version": "3.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz",
|
||||||
|
|||||||
@ -7,6 +7,8 @@
|
|||||||
"dev:api": "cross-env NODE_ENV=development nodemon src/infra/http/server.js",
|
"dev:api": "cross-env NODE_ENV=development nodemon src/infra/http/server.js",
|
||||||
"start:worker": "cross-env NODE_ENV=production node src/infra/cron/sync.cron.js",
|
"start:worker": "cross-env NODE_ENV=production node src/infra/cron/sync.cron.js",
|
||||||
"dev:worker": "cross-env NODE_ENV=development nodemon src/infra/cron/sync.cron.js",
|
"dev:worker": "cross-env NODE_ENV=development nodemon src/infra/cron/sync.cron.js",
|
||||||
|
"start:watchdog": "cross-env NODE_ENV=production node src/infra/cron/observer.cron.js",
|
||||||
|
"dev:watchdog": "cross-env NODE_ENV=development nodemon src/infra/cron/observer.cron.js",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"postinstall": "node -e \"const fs = require('fs'); if (fs.existsSync('.env') && !fs.existsSync('.env.development')) { fs.copyFileSync('.env', '.env.development'); console.log('✅ .env.development criado a partir do .env'); }\""
|
"postinstall": "node -e \"const fs = require('fs'); if (fs.existsSync('.env') && !fs.existsSync('.env.development')) { fs.copyFileSync('.env', '.env.development'); console.log('✅ .env.development criado a partir do .env'); }\""
|
||||||
},
|
},
|
||||||
@ -19,6 +21,7 @@
|
|||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"mysql2": "^3.15.2",
|
"mysql2": "^3.15.2",
|
||||||
"node-cron": "^4.2.1",
|
"node-cron": "^4.2.1",
|
||||||
|
"nodemailer": "^7.0.12",
|
||||||
"pg": "^8.16.3",
|
"pg": "^8.16.3",
|
||||||
"pm2": "^6.0.13",
|
"pm2": "^6.0.13",
|
||||||
"qs": "^6.14.0",
|
"qs": "^6.14.0",
|
||||||
|
|||||||
18
src/infra/cron/observer.cron.js
Normal file
18
src/infra/cron/observer.cron.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// src/infra/cron/observer.cron.js
|
||||||
|
|
||||||
|
const cron = require('node-cron')
|
||||||
|
const logger = require('../../shared/utils/logger')
|
||||||
|
const runWatchdog = require('../../modules/watchdog/job/job')
|
||||||
|
|
||||||
|
logger.info('[CRON] 🐶 Watchdog cron iniciado')
|
||||||
|
|
||||||
|
cron.schedule('*/30 * * * *', async () => {
|
||||||
|
logger.info('[CRON] 🐶 Watchdog executando verificação')
|
||||||
|
|
||||||
|
try {
|
||||||
|
await runWatchdog()
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('[CRON] ❌ Erro no Watchdog', { error })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
@ -55,20 +55,27 @@ async function updateSyncDataCreated(hubsoftId, glpiId) {
|
|||||||
*/
|
*/
|
||||||
async function getSyncIdByHubsoftId(hubsoftTicketId) {
|
async function getSyncIdByHubsoftId(hubsoftTicketId) {
|
||||||
const query = `
|
const query = `
|
||||||
SELECT id, glpi_ticket_id
|
SELECT
|
||||||
|
id,
|
||||||
|
glpi_ticket_id,
|
||||||
|
status_sync
|
||||||
FROM sync_data
|
FROM sync_data
|
||||||
WHERE hubsoft_ticket_id = $1;
|
WHERE hubsoft_ticket_id = $1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { rows } = await db.query(query, [hubsoftTicketId]);
|
const { rows } = await db.query(query, [hubsoftTicketId]);
|
||||||
return rows[0] || null;
|
return rows.length ? rows[0] : null;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logError('Erro ao buscar sync_data por hubsoft_ticket_id', err);
|
logError(
|
||||||
|
`Erro ao buscar sync_data para hubsoft_ticket_id=${hubsoftTicketId}`,
|
||||||
|
err
|
||||||
|
);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function lockTicketForClosing(syncId) {
|
async function lockTicketForClosing(syncId) {
|
||||||
const query = `
|
const query = `
|
||||||
UPDATE sync_data
|
UPDATE sync_data
|
||||||
|
|||||||
@ -160,6 +160,8 @@ async function updateCloseMessage(hubTicketId, message) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
insertTickets,
|
insertTickets,
|
||||||
fetchPendingTickets,
|
fetchPendingTickets,
|
||||||
|
|||||||
87
src/infra/db/repositories/hubglpi/watchdog.repository.js
Normal file
87
src/infra/db/repositories/hubglpi/watchdog.repository.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
// src/infra/db/repositories/hubglpi/watchdog.repository.js
|
||||||
|
|
||||||
|
const db = require('../connections/hubglpi.pg.js');
|
||||||
|
const { logError } = require('../../../../shared/utils/logger.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se a notificação já foi enviada com sucesso
|
||||||
|
*/
|
||||||
|
async function notificationAlreadySent(hubsoftTicketId) {
|
||||||
|
const query = `
|
||||||
|
SELECT 1
|
||||||
|
FROM watchdog_notifications
|
||||||
|
WHERE ticket_id = $1
|
||||||
|
AND status = 'SENT'
|
||||||
|
LIMIT 1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { rowCount } = await db.query(query, [hubsoftTicketId]);
|
||||||
|
return rowCount > 0;
|
||||||
|
} catch (error) {
|
||||||
|
logError('Erro ao verificar notificação já enviada', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marca tickets como notificados com sucesso
|
||||||
|
*/
|
||||||
|
async function markNotificationsAsSent(hubsoftTicketIds) {
|
||||||
|
if (!hubsoftTicketIds || hubsoftTicketIds.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = `
|
||||||
|
INSERT INTO watchdog_notifications (ticket_id, notified_at, status)
|
||||||
|
SELECT
|
||||||
|
unnest($1::bigint[]),
|
||||||
|
NOW(),
|
||||||
|
'SENT'
|
||||||
|
ON CONFLICT (ticket_id)
|
||||||
|
DO UPDATE SET
|
||||||
|
notified_at = EXCLUDED.notified_at,
|
||||||
|
status = 'SENT';
|
||||||
|
`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.query(query, [hubsoftTicketIds]);
|
||||||
|
} catch (error) {
|
||||||
|
logError('Erro ao marcar notificações como enviadas', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marca tickets como falha de notificação
|
||||||
|
*/
|
||||||
|
async function markNotificationsAsFailed(hubsoftTicketIds) {
|
||||||
|
if (!hubsoftTicketIds || hubsoftTicketIds.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = `
|
||||||
|
INSERT INTO watchdog_notifications (ticket_id, notified_at, status)
|
||||||
|
SELECT
|
||||||
|
unnest($1::bigint[]),
|
||||||
|
NOW(),
|
||||||
|
'FAILED'
|
||||||
|
ON CONFLICT (ticket_id)
|
||||||
|
DO UPDATE SET
|
||||||
|
notified_at = EXCLUDED.notified_at,
|
||||||
|
status = 'FAILED';
|
||||||
|
`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.query(query, [hubsoftTicketIds]);
|
||||||
|
} catch (error) {
|
||||||
|
logError('Erro ao marcar notificações como falha', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
notificationAlreadySent,
|
||||||
|
markNotificationsAsSent,
|
||||||
|
markNotificationsAsFailed
|
||||||
|
};
|
||||||
@ -95,7 +95,40 @@ async function getTicketsByTipo({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getTicketsClosedSince(thresholdDate) {
|
||||||
|
try {
|
||||||
|
const mundialeUserId = process.env.HUBSOFT_MUNDIALE_USER_ID;
|
||||||
|
|
||||||
|
if (!mundialeUserId) {
|
||||||
|
throw new Error('HUBSOFT_MUNDIALE_USER_ID não definido no .env');
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = `
|
||||||
|
SELECT
|
||||||
|
id_atendimento,
|
||||||
|
protocolo,
|
||||||
|
data_fechamento AS hubsoft_closed_at
|
||||||
|
FROM atendimento
|
||||||
|
WHERE id_usuario_abertura = $1
|
||||||
|
AND data_fechamento > $2
|
||||||
|
`;
|
||||||
|
|
||||||
|
const params = [
|
||||||
|
Number(mundialeUserId),
|
||||||
|
thresholdDate
|
||||||
|
];
|
||||||
|
|
||||||
|
const { rows } = await db.query(query, params);
|
||||||
|
return rows;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logError('Erro ao buscar tickets fechados HubSoft (Watchdog)', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getTicketsByTipo
|
getTicketsByTipo,
|
||||||
|
getTicketsClosedSince
|
||||||
}
|
}
|
||||||
|
|||||||
50
src/infra/mail/sender.js
Normal file
50
src/infra/mail/sender.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// src/infra/mail/sender.js
|
||||||
|
|
||||||
|
const nodemailer = require('nodemailer');
|
||||||
|
const logger = require('../../shared/utils/logger.js');
|
||||||
|
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
host: process.env.MAIL_HOST,
|
||||||
|
port: Number(process.env.MAIL_PORT),
|
||||||
|
secure: process.env.MAIL_SECURE === 'true',
|
||||||
|
auth: {
|
||||||
|
user: process.env.MAIL_USER,
|
||||||
|
pass: process.env.MAIL_PASS
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
async function send({ subject, bodyEmail, recipients, cc = [] }) {
|
||||||
|
if (!recipients || !recipients.length) {
|
||||||
|
throw new Error('[MAIL] Nenhum destinatário informado');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!subject) {
|
||||||
|
throw new Error('[MAIL] Assunto do email não informado');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bodyEmail) {
|
||||||
|
throw new Error('[MAIL] Corpo do email não informado');
|
||||||
|
}
|
||||||
|
|
||||||
|
const mailOptions = {
|
||||||
|
from: process.env.MAIL_FROM,
|
||||||
|
to: recipients.join(','),
|
||||||
|
cc: cc.length ? cc.join(',') : undefined,
|
||||||
|
subject,
|
||||||
|
html: bodyEmail
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const info = await transporter.sendMail(mailOptions);
|
||||||
|
|
||||||
|
logger.info(`[MAIL] Email enviado com sucesso. MessageId=${info.messageId}`);
|
||||||
|
|
||||||
|
return info;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('[MAIL] Erro ao enviar email', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { send };
|
||||||
58
src/modules/watchdog/job/job.js
Normal file
58
src/modules/watchdog/job/job.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// src/modules/watchdog/job/job.js
|
||||||
|
|
||||||
|
const logger = require('../../../shared/utils/logger')
|
||||||
|
const repository = require('../repository/watchdog.repository.js')
|
||||||
|
const model = require('../model/email.model.js')
|
||||||
|
|
||||||
|
async function runWatchdog() {
|
||||||
|
|
||||||
|
logger.info('[WATCHDOG] [JOB] Coletando chamados fechados há 31 minutos')
|
||||||
|
|
||||||
|
const thresholdDate = new Date(Date.now() - 31 * 60 * 1000)
|
||||||
|
|
||||||
|
const closedTickets = await repository.getClosedTicketsSince(thresholdDate)
|
||||||
|
logger.info(`[WATCHDOG] [JOB] Encontrados ${closedTickets.length} chamados fechados`)
|
||||||
|
|
||||||
|
for (const ticket of closedTickets) {
|
||||||
|
|
||||||
|
const hubGlpiTicket = await repository.checkTicketInHubGlpi(ticket.id_atendimento)
|
||||||
|
|
||||||
|
if (!hubGlpiTicket.exists) {
|
||||||
|
logger.info(`[WATCHDOG] [JOB] Chamado ${ticket.id_atendimento} não encontrado no HubGlpi. Ignorando.`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hubGlpiTicket.status === 'closed') {
|
||||||
|
logger.info(`[WATCHDOG] [JOB] Chamado ${ticket.id_atendimento} já está fechado no HubGlpi. Ignorando.`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (await repository.notificationAlreadySent(ticket.id_atendimento)) {
|
||||||
|
logger.info(`[WATCHDOG] [JOB] Notificação já enviada para o chamado ${ticket.id_atendimento}.`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
await repository.insertAsPending(ticket.id_atendimento)
|
||||||
|
|
||||||
|
}
|
||||||
|
const ticketsToNotify = await repository.getPendingTicketsForNotification()
|
||||||
|
logger.info(`[WATCHDOG] [JOB] ${ticketsToNotify.length} chamados pendentes para notificação.`)
|
||||||
|
|
||||||
|
const payload = await model.prepareNotificationPayload(ticketsToNotify)
|
||||||
|
|
||||||
|
if (!payload.length) {
|
||||||
|
logger.info('[WATCHDOG] [JOB] Nenhuma notificação para enviar')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await repository.sendClosureNotifications(payload)
|
||||||
|
await repository.markNotificationsAsSent(ticketsToNotify)
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('[WATCHDOG] Erro ao enviar notificações', err)
|
||||||
|
await repository.markNotificationsAsFailed(ticketsToNotify)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`[WATCHDOG] [JOB] Enviadas ${payload.length} notificações`)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = { runWatchdog}
|
||||||
72
src/modules/watchdog/model/email.model.js
Normal file
72
src/modules/watchdog/model/email.model.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// src/modules/watchdog/model/email.model.js
|
||||||
|
|
||||||
|
function prepareNotificationPayload(tickets) {
|
||||||
|
return {
|
||||||
|
subject: buildSubject(tickets),
|
||||||
|
bodyEmail: buildBodyEmail(tickets),
|
||||||
|
recipients: getRecipients(),
|
||||||
|
cc: getCc()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSubject(tickets) {
|
||||||
|
|
||||||
|
if (tickets.length === 0) {
|
||||||
|
return '[GOLEIRO] Nenhum chamado foi agarrado';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `🧤 [GOLEIRO] ${tickets.length} chamados foram agarrados`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildBodyEmail(tickets) {
|
||||||
|
let body = `
|
||||||
|
<p>🚨 <strong>Atenção!</strong></p>
|
||||||
|
<p>O goleiro defendeu os seguintes chamados:</p>
|
||||||
|
<p>Esses chamados foram <strong>fechados no Hubsoft</strong>, mas ainda constam como <strong>abertos no GLPI</strong>.</p>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
`;
|
||||||
|
|
||||||
|
tickets.forEach(ticket => {
|
||||||
|
body += `
|
||||||
|
<li>
|
||||||
|
Hubsoft ID: <strong>${ticket.hubsoft_ticket_id}</strong><br>
|
||||||
|
GLPI ID: <strong>${ticket.glpi_ticket_id ?? 'não encontrado'}</strong><br>
|
||||||
|
Fechado em: ${formatDate(ticket.hubsoft_closed_at)}
|
||||||
|
</li>
|
||||||
|
<br>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
body += `
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<p>⚽ Favor verificar e alinhar os status no GLPI.</p>
|
||||||
|
<p><em>Watchdog Hub × GLPI</em></p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function formatDate(date) {
|
||||||
|
try {
|
||||||
|
return new Date(date).toLocaleString('pt-BR');
|
||||||
|
} catch {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getRecipients() {
|
||||||
|
return process.env.WATCHDOG_RECIPIENT_EMAILS?.split(',') || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCc() {
|
||||||
|
return process.env.WATCHDOG_CC_EMAILS?.split(',') || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
prepareNotificationPayload
|
||||||
|
};
|
||||||
69
src/modules/watchdog/repository/watchdog.repository.js
Normal file
69
src/modules/watchdog/repository/watchdog.repository.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Facade de acesso a dados do Watchdog
|
||||||
|
// Centraliza interações com Hubsoft, HubGLPI e controle de notificações
|
||||||
|
|
||||||
|
const wdRepository = require('../../../infra/db/repositories/hubglpi/watchdog.repository.js');
|
||||||
|
const hubsoftTicketsRepo = require('../../../infra/db/repositories/hubsoft/tickets.repository.js');
|
||||||
|
const hubglpiSyncRepo = require('../../../infra/db/repositories/hubglpi/sync.repository.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Busca tickets encerrados no Hubsoft a partir de uma data
|
||||||
|
*/
|
||||||
|
async function getClosedTicketsSince(thresholdDate) {
|
||||||
|
return hubsoftTicketsRepo.getTicketsClosedSince(thresholdDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se um ticket do Hubsoft existe no GLPI e seu status de sync
|
||||||
|
*/
|
||||||
|
async function checkTicketInHubGlpi(hubsoftTicketId) {
|
||||||
|
const syncData = await hubglpiSyncRepo.getSyncByHubsoftTicketId(hubsoftTicketId);
|
||||||
|
|
||||||
|
if (!syncData) {
|
||||||
|
return {
|
||||||
|
exists: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
exists: true,
|
||||||
|
glpiTicketId: syncData.glpi_ticket_id,
|
||||||
|
status: syncData.status_sync
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se a notificação de encerramento já foi enviada
|
||||||
|
*/
|
||||||
|
async function notificationAlreadySent(hubsoftTicketId) {
|
||||||
|
return wdRepository.notificationAlreadySent(hubsoftTicketId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Envia notificações de encerramento
|
||||||
|
*/
|
||||||
|
async function sendClosureNotifications(payload) {
|
||||||
|
return enviadordeEmailNaoseicomoserafeito.sendClosureNotifications(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marca notificações como enviadas com sucesso
|
||||||
|
*/
|
||||||
|
async function markNotificationsAsSent(hubsoftTicketIds) {
|
||||||
|
return wdRepository.markNotificationsAsSent(hubsoftTicketIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marca notificações como falhas
|
||||||
|
*/
|
||||||
|
async function markNotificationsAsFailed(hubsoftTicketIds, error = null) {
|
||||||
|
return wdRepository.markNotificationsAsFailed(hubsoftTicketIds, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getClosedTicketsSince,
|
||||||
|
checkTicketInHubGlpi,
|
||||||
|
notificationAlreadySent,
|
||||||
|
sendClosureNotifications,
|
||||||
|
markNotificationsAsSent,
|
||||||
|
markNotificationsAsFailed
|
||||||
|
};
|
||||||
0
src/shared/utils/sendEmail.js
Normal file
0
src/shared/utils/sendEmail.js
Normal file
Loading…
Reference in New Issue
Block a user