HOTFIX: Correção do Bug Em PROD
- Travas foram adicionadas para que a reabertura nao seja feita - Regra de desatribuição de usuario caso seja "Fora de Escopo"
This commit is contained in:
parent
686c2f0a54
commit
00b573d60d
@ -37,7 +37,7 @@ async function main() {
|
|||||||
logInfo('Monitor de encerramento definitivo GLPI desabilitado (ENABLE_GLPI_CLOSE_CRON=false).');
|
logInfo('Monitor de encerramento definitivo GLPI desabilitado (ENABLE_GLPI_CLOSE_CRON=false).');
|
||||||
}
|
}
|
||||||
|
|
||||||
await processCommentsController();
|
|
||||||
|
|
||||||
if (statusSyncEnabled) {
|
if (statusSyncEnabled) {
|
||||||
await processStatusAndClosureController();
|
await processStatusAndClosureController();
|
||||||
@ -45,6 +45,8 @@ async function main() {
|
|||||||
logInfo('Sincronizacao legada de status desabilitada (ENABLE_STATUS_SYNC=false).');
|
logInfo('Sincronizacao legada de status desabilitada (ENABLE_STATUS_SYNC=false).');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await processCommentsController();
|
||||||
|
|
||||||
logInfo('Ciclo de sincronizacao concluido com sucesso.');
|
logInfo('Ciclo de sincronizacao concluido com sucesso.');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logInfo('Erro na aplicacao:', error);
|
logInfo('Erro na aplicacao:', error);
|
||||||
|
|||||||
@ -12,6 +12,12 @@ const ensureReopenFromSNWhenGlpiSolved = async (ticket) => {
|
|||||||
return { reopened: false, glpiSolved: false };
|
return { reopened: false, glpiSolved: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// REGRA DE CONTENÇÃO: Só reabre se o monitoramento de status estiver ativo.
|
||||||
|
// Se o usuário desligou o sync de status, ele quer controle manual.
|
||||||
|
if (process.env.ENABLE_STATUS_SYNC === 'false') {
|
||||||
|
return { reopened: false, glpiSolved: true };
|
||||||
|
}
|
||||||
|
|
||||||
// CORREÇÃO: Verificar se o ticket foi fechado recentemente.
|
// CORREÇÃO: Verificar se o ticket foi fechado recentemente.
|
||||||
// Se o status já é 'solved', significa que o processGlpiClosureController
|
// Se o status já é 'solved', significa que o processGlpiClosureController
|
||||||
// tentou fechar recentemente. O SN pode ainda estar processando o close,
|
// tentou fechar recentemente. O SN pode ainda estar processando o close,
|
||||||
|
|||||||
@ -120,10 +120,10 @@ const processStatusAndClosureController = async () => {
|
|||||||
await addServiceNowClosureNoteToGlpi(ticket, collaboratorName, 'encerrado');
|
await addServiceNowClosureNoteToGlpi(ticket, collaboratorName, 'encerrado');
|
||||||
await TicketSyncModel.updateStatusAndSourceLast(ticket.sn_ticket_id, 'closed', 'closed', 'SNOW');
|
await TicketSyncModel.updateStatusAndSourceLast(ticket.sn_ticket_id, 'closed', 'closed', 'SNOW');
|
||||||
} else {
|
} else {
|
||||||
// Se estiver Solucionado (5) ou em qualquer outro estado, permite a atualização (reabertura).
|
// Sincronização passiva: não propagamos status intermediários (Em Espera, Atribuído, etc.)
|
||||||
await TicketGlpiModel.updateStatus(ticket.glpi_ticket_id, snCurrentStatus);
|
// para o GLPI para evitar conflitos com a operação manual sugerida pelo usuário.
|
||||||
logInfo(`Status do ticket GLPI ${ticket.glpi_ticket_id} atualizado para '${snCurrentStatus}'.`);
|
logInfo(`SN ${ticketSnDetails.ticket_number} em status '${snCurrentStatus}'. Ignorando atualização automática de status no GLPI (Regra de Contenção).`);
|
||||||
await TicketSyncModel.updateStatusAndSourceLast(ticket.sn_ticket_id, 'synced', 'synced', 'GLPI'); // Passa o bastão
|
await TicketSyncModel.updateStatusAndSourceLast(ticket.sn_ticket_id, 'synced', 'synced', 'GLPI');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ================= LÓGICA GLPI -> SNOW =================
|
// ================= LÓGICA GLPI -> SNOW =================
|
||||||
@ -144,6 +144,7 @@ const processStatusAndClosureController = async () => {
|
|||||||
const justification = solution?.content ? stripHTML(solution.content) : 'Ticket marcado como "Fora do escopo" no GLPI.';
|
const justification = solution?.content ? stripHTML(solution.content) : 'Ticket marcado como "Fora do escopo" no GLPI.';
|
||||||
await addWorkNoteToServiceNow(ticket.sn_ticket_id, `Ticket não será mais exibido para equipe Sothis, com a justificativa: ${justification}`);
|
await addWorkNoteToServiceNow(ticket.sn_ticket_id, `Ticket não será mais exibido para equipe Sothis, com a justificativa: ${justification}`);
|
||||||
await TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'closed', 'ignored');
|
await TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'closed', 'ignored');
|
||||||
|
await TicketGlpiModel.unassignGroupFromTicket(ticket.glpi_ticket_id);
|
||||||
await TicketGlpiModel.forceCloseTicket(ticket.glpi_ticket_id);
|
await TicketGlpiModel.forceCloseTicket(ticket.glpi_ticket_id);
|
||||||
} else {
|
} else {
|
||||||
// REGRA DE NEGÓCIO: Não pode fechar um ticket que está "Em Espera" ou "Aguardando Atendimento" no SN.
|
// REGRA DE NEGÓCIO: Não pode fechar um ticket que está "Em Espera" ou "Aguardando Atendimento" no SN.
|
||||||
@ -171,23 +172,12 @@ const processStatusAndClosureController = async () => {
|
|||||||
if (ticket.glpi_sync_status !== 'closed') {
|
if (ticket.glpi_sync_status !== 'closed') {
|
||||||
await TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'closed', ticket.sn_sync_status);
|
await TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'closed', ticket.sn_sync_status);
|
||||||
}
|
}
|
||||||
} else {
|
// Sincronização passiva: não propagamos status intermediários (reaberturas parciais, etc.)
|
||||||
// Se o status for de um ticket aberto (reabertura, etc.), propaga a mudança.
|
// do GLPI para o SN, exceto para fechamentos definitivos já tratados acima.
|
||||||
const glpiStatusAsString = TicketGlpiModel.mapGlpiStatusToString(glpiCurrentStatus);
|
logInfo(`Ticket GLPI ${ticket.glpi_ticket_id} em status '${glpiCurrentStatus}'. Ignorando atualização automática de status no SN (Regra de Contenção).`);
|
||||||
if (glpiStatusAsString !== snCurrentStatus) {
|
|
||||||
if (snCurrentStatus === 'Encerrado' || snCurrentStatus === 'Encerrado - Omitido'){
|
|
||||||
logInfo(`SN ${ticketSnDetails.ticket_number} foi Fechado. Sincronizando para GLPI...`);
|
|
||||||
await TicketGlpiModel.forceCloseTicket(ticket.glpi_ticket_id);
|
|
||||||
await TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'closed', 'closed');
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
logInfo(`Diferença de status detectada (GLPI: ${glpiStatusAsString}, SN: ${snCurrentStatus}). Sincronizando...`);
|
|
||||||
await updateStatusInServiceNow(ticket.sn_ticket_id, glpiStatusAsString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Passa o bastão de volta para o SN se a ação não for de finalização
|
// Passa o bastão de volta para o SN se a ação não for de finalização
|
||||||
if (glpiCurrentStatus <= 4) {
|
if (glpiCurrentStatus && glpiCurrentStatus <= 4) {
|
||||||
await TicketSyncModel.updateStatusAndSourceLast(ticket.sn_ticket_id, 'synced', 'synced', 'SNOW');
|
await TicketSyncModel.updateStatusAndSourceLast(ticket.sn_ticket_id, 'synced', 'synced', 'SNOW');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -476,6 +476,21 @@ class TicketGlpiModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async unassignGroupFromTicket(ticketId) {
|
||||||
|
const connection = await glpiPool.getConnection();
|
||||||
|
const groupId = parseInt(process.env.GLPI_DEFAULT_GROUP_ID, 10) || 30;
|
||||||
|
try {
|
||||||
|
const query = `DELETE FROM glpi_groups_tickets WHERE tickets_id = ? AND groups_id = ?`;
|
||||||
|
await connection.execute(query, [ticketId, groupId]);
|
||||||
|
logInfo(`✅ Grupo ${groupId} removido do ticket GLPI ${ticketId}.`);
|
||||||
|
} catch (error) {
|
||||||
|
logError(error, `❌ Erro ao remover grupo ${groupId} do ticket ${ticketId} no GLPI`);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
if (connection) connection.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user