- feito ajuste do fluxo GLPI->SN para tratar status 5 (resolve no SN) e status 6 (aviso definitivo e retirada de monitoramento) - feito ajuste de reabertura SN->GLPI quando ticket estiver solved no GLPI - feito ajuste de data/hora de comments/worknotes para evitar inversão de data - feito ajuste de vínculo Ticket Externo no SN com validação de persistência e retry idempotente - feito ajuste de filtro de journals para ignorar SOTHIS.CAOA e admin.caoa por configuração - feito inclusão de monitor dedicado de encerramento definitivo (processGlpiClosureController) - feito atualização de flags de controle (ENABLE_STATUS_SYNC, ENABLE_GLPI_CLOSE_CRON, ENABLE_GLPI_WEBHOOK) - feito atualização da documentação (README, docs/regrasdenegocio.md, docs/fluxo.md, docs/casosdeuso.md, docs/checklist.md)
94 lines
4.9 KiB
JavaScript
94 lines
4.9 KiB
JavaScript
const TicketSyncModel = require('../models/ticketSyncModel');
|
|
const TicketGlpiModel = require('../models/ticketGlpiModel');
|
|
const TicketUpdateModel = require('../models/ticketUpdateModel');
|
|
const TicketSnModel = require('../models/ticketSnModel');
|
|
const { addCommentToServiceNow, addWorkNoteToServiceNow, updateStatusInServiceNow, fetchTicketLiveStatusFromServiceNow, closeTicketInServiceNow } = require('../services/servicenowService');
|
|
const { stripHTML } = require('../utils/commentSanitizer');
|
|
const { logInfo, logError } = require('../utils/logger');
|
|
|
|
const DEFINITIVE_CLOSE_NOTICE = 'Chamado encerrado definitivamente. Reabertura deste chamado nao sera atendida. Caso necessario, abra um novo chamado.';
|
|
const outOfScopeSolutionTypeId = Number(process.env.GLPI_OOS_SOLUTION_TYPE_ID || 27);
|
|
|
|
const processGlpiClosureController = async () => {
|
|
try {
|
|
const tickets = await TicketSyncModel.getTicketsForClosureMonitor();
|
|
if (!tickets.length) {
|
|
logInfo('Nenhum ticket ativo para monitoramento de fechamento definitivo no GLPI.');
|
|
return;
|
|
}
|
|
|
|
for (const ticket of tickets) {
|
|
try {
|
|
const glpiStatus = await TicketGlpiModel.getTicketStatus(ticket.glpi_ticket_id);
|
|
if (glpiStatus !== 5 && glpiStatus !== 6) {
|
|
continue;
|
|
}
|
|
|
|
const ticketSn = await TicketSnModel.findById(ticket.sn_ticket_id);
|
|
if (!ticketSn) {
|
|
continue;
|
|
}
|
|
|
|
const liveSnStatus = await fetchTicketLiveStatusFromServiceNow(ticketSn.sys_id, ticketSn.tipo);
|
|
if (glpiStatus === 5) {
|
|
if (ticket.sn_sync_status === 'solved') {
|
|
continue;
|
|
}
|
|
|
|
const solution = await TicketGlpiModel.getTicketSolution(ticket.glpi_ticket_id);
|
|
if (solution?.solutiontypes_id === outOfScopeSolutionTypeId) {
|
|
const justification = solution?.content ? stripHTML(solution.content) : 'Ticket marcado como fora do escopo.';
|
|
await addWorkNoteToServiceNow(ticket.sn_ticket_id, `Ticket nao sera mais exibido para equipe Sothis, com a justificativa: ${justification}`);
|
|
await TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'closed', 'ignored');
|
|
await TicketGlpiModel.forceCloseTicket(ticket.glpi_ticket_id);
|
|
continue;
|
|
}
|
|
|
|
if (liveSnStatus === 'Em Espera' || liveSnStatus === 'Aguardando Atendimento') {
|
|
await updateStatusInServiceNow(ticket.sn_ticket_id, 'Em Atendimento');
|
|
}
|
|
|
|
const closeNotes = solution?.content ? stripHTML(solution.content) : 'Resolvido via integracao GLPI.';
|
|
const resolvedAt = solution?.date_mod || new Date();
|
|
await closeTicketInServiceNow(ticket.sn_ticket_id, closeNotes, resolvedAt);
|
|
await TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'solved', 'solved');
|
|
logInfo(`Ticket GLPI ${ticket.glpi_ticket_id} solucionado e refletido no SN.`);
|
|
continue;
|
|
}
|
|
|
|
const sourceId = `glpi_closed_notice:${ticket.glpi_ticket_id}`;
|
|
const alreadyNotified = await TicketUpdateModel.getBySourceId(sourceId);
|
|
if (!alreadyNotified) {
|
|
const commentSent = await addCommentToServiceNow(ticket.sn_ticket_id, DEFINITIVE_CLOSE_NOTICE);
|
|
if (commentSent) {
|
|
await TicketUpdateModel.insert({
|
|
ticket_sync_id: ticket.id,
|
|
update_type: 'close_notice',
|
|
source_system: 'glpi',
|
|
content: DEFINITIVE_CLOSE_NOTICE,
|
|
author: 'integration',
|
|
created_at: new Date(),
|
|
updated_at: new Date(),
|
|
source_id: sourceId,
|
|
destiny_id: 'done'
|
|
});
|
|
}
|
|
}
|
|
|
|
// Regra nova: nao altera status no SN ao fechar definitivamente no GLPI.
|
|
// Apenas registra comentario e remove do monitoramento local.
|
|
await TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'closed', 'closed');
|
|
logInfo(`Ticket GLPI ${ticket.glpi_ticket_id} encerrado definitivamente e refletido no SN.`);
|
|
} catch (error) {
|
|
logError(error, `Falha ao processar fechamento definitivo GLPI para ticket ${ticket.glpi_ticket_id}`);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
logError(error, 'Erro geral no monitoramento de fechamento definitivo GLPI');
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
processGlpiClosureController
|
|
};
|