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 };