diff --git a/app.js b/app.js index 60e4104..4470ffc 100644 --- a/app.js +++ b/app.js @@ -1,3 +1,3 @@ const hubsoftController = require('./controller/processController.js'); // Inicia o processamento dos atendimentos -hubsoftController.processaAtendimentos(); \ No newline at end of file +hubsoftController.processaAtendimentos(skipHubSoft = true); \ No newline at end of file diff --git a/controller/processController.js b/controller/processController.js index 2bdb27c..514c368 100644 --- a/controller/processController.js +++ b/controller/processController.js @@ -1,49 +1,224 @@ // controller/processController.js const hubsoftModel = require('../model/hubsoftModel.js'); const hubglpiModel = require('../model/hubglpiModel.js'); -const hubsoftService = require('../services/hubsoftService.js'); -const { logError, logInfo} = require('../utils/logger'); +const glpiModel = require('../model/glpiModel.js'); +const { logError, logInfo } = require('../utils/logger'); -const processaAtendimentos = async () => { - +// ================================================================================ +// Constantes e Configurações +// ================================================================================ +const statusAtendimentoHubGlpi = { + 1: 'Pendente', + 2: 'Em atendimento', + 3: 'Resolvido', + 31: 'Pendente', + 32: 'Pendente', + 33: 'Novo' +}; + +const statusAtendimentoGLPI = { + 'Novo': 1, + 'Pendente': 4, + 'Em atendimento': 2, + 'Resolvido': 5 +}; + +// ================================================================================ +// Funções Utilitárias +// ================================================================================ + + +// Formata a descrição do ticket em HTML +const formatDescription = (ticketData) => { + const { cliente_nome, codigo_cliente, servico_nome, ticket_mundiale, protocolo_hub, description } = ticketData; + + const tableRows = ` + Nome do Cliente:${cliente_nome} + Codigo Cliente:${codigo_cliente} + Serviço:${servico_nome} + Ticket Number (Mundiale):${ticket_mundiale || 'N/A'} + Protocolo Hub:${protocolo_hub || 'N/A'} + `; + + const htmlDescription = ` + + + + + + ${tableRows} +
CampoValor
+
+ Descrição: +
+ ${description || 'N/A'} + `; + + + console.log(htmlDescription); + + return htmlDescription; +}; + + +// Gera o entity_Name baseado nos campos do ticket +const generateEntityName = (ticketData) => { + const codigoClienteParts = (ticketData.codigo_cliente || '').split('-'); + const codigoServicoFirstPart = (ticketData.codigo_servico || '').split('-')[0]?.trim() || ''; + const clienteNome = ticketData.cliente_nome || ''; + + return [...codigoClienteParts, codigoServicoFirstPart, clienteNome].join(''); +}; + + +// Formata os dados do ticket para o GLPI +const formatTicketDataForGlpi = async (ticketData) => { + const formattedData = { + ...ticketData, + status_atendimento: statusAtendimentoGLPI[ticketData.status_atendimento] || 1, + date_mod: new Date(), + user_id_recipient: 971, + descricao_abertura: toString(formatDescription(ticketData)), + urgency: 3, + impact: 3, + priority: 3, + type: 1, + itilcategories_id: 1, + date_creation: new Date(), + entidades_id: await glpiModel.selectEntityId() //TODO: Implementar a busca da entidade + }; + return formattedData; +}; + +// ================================================================================ +// Funções de Integração +// ================================================================================ + +const createGlpiTicket = async (ticketData) => { try { - const atendimentosDB = await hubsoftModel.getAtendimentosFromDB(); - - // Insere os dados do banco do Hubsoft nas tabelas hubsoft_tickets e sync_data - for (const atendimento of atendimentosDB) { - console.log('Processando atendimento:', atendimento); - const ticketData = { - id_atendimento: atendimento.id_atendimento, - id_atendimento_status: atendimento.id_atendimento_status, - protocolo_hub: atendimento.protocolo, - descricao_abertura: atendimento.descricao_abertura, - data_cadastro: atendimento.data_cadastro, - cliente_nome: atendimento.nome_contato, - codigo_cliente: atendimento.codigo_cliente, - descricao: atendimento.descricao - }; + const formattedTicketData = await formatTicketDataForGlpi(ticketData); + console.log('Dados do ticket formatados para GLPI:', formattedTicketData); + // const glpiTicket = await glpiModel.insertTicket(formattedTicketData); + // console.log('Ticket criado no GLPI:', glpiTicket); - const insertedTicket = await hubglpiModel.insertTicket(ticketData); - console.log('Ticket inserido:', insertedTicket); + // const updateSyncData = await hubglpiModel.update_syncData(glpiTicket.id, ticketData.id_atendimento); + // logInfo('Sync Data atualizado com o ID do ticket do GLPI:', updateSyncData); - - - console.log('Inserindo Sync Data para o atendimento ID:', ticketData.id_atendimento); - - const insertSyncData = await hubglpiModel.insertSyncData(ticketData.id_atendimento) - console.log('Sync Data inserido:', insertSyncData); - - - - } } catch (error) { - console.error('Erro ao processar atendimentos:', error); -} + console.error('Erro ao criar ticket no GLPI:', error); + // const updateSyncDataError = await //hubglpiModel.update_syncaDataError(error.message, ticketData.id_atendimento) + logError('Erro ao criar ticket no GLPI. Sync Data atualizado com a mensagem de erro: ', /*updateSyncDataError*/ error); + } +}; + +const processTicketFromHubSoft = async (atendimento) => { + const ticketData = { + id_atendimento: atendimento.id_atendimento, + id_atendimento_status: atendimento.id_atendimento_status, + codigo_servico: String(atendimento.id_cliente_servico || ''), // Garante que seja uma string + protocolo_hub: atendimento.protocolo, + servico_nome: atendimento.descricao, + descricao_abertura: atendimento.descricao_abertura, + data_cadastro: atendimento.data_cadastro, + cliente_nome: atendimento.nome_contato, + codigo_cliente: atendimento.codigo_cliente, + descricao: atendimento.descricao + }; + + ticketData.status_atendimento = statusAtendimentoHubGlpi[ticketData.id_atendimento_status] || ticketData.id_atendimento_status; + + console.log(typeof ticketData.descricao_abertura) + console.log(ticketData.descricao_abertura) + + + ticketData.descricao_abertura = ticketData.descricao_abertura.replace(/[^0-9]/g, ''); + ticketData.ticket_mundiale = parseInt(ticketData.descricao_abertura) || null; + + return ticketData; +}; + +const saveTicketToHubGlpi = async (ticketData) => { + try { + const insertedTicket = await hubglpiModel.insertTicket(ticketData); + console.log('Ticket inserido/atualizado na tabela hubsoft_tickets:', insertedTicket); + + const insertSyncData = await hubglpiModel.insertSyncData(ticketData.id_atendimento); + console.log('Dados inseridos/atualizados na tabela sync_data:', insertSyncData); + + return insertedTicket; + } catch (error) { + console.error('Erro ao salvar ticket no hubglpi:', error); + throw error; // Rejeita a promise para que o erro seja tratado no nível superior + } +}; + +const processAtendimento = async (ticketData) => { + try { + + await createGlpiTicket(ticketData); + + } catch (error) { + console.error(`Erro ao processar atendimento ${id_atendimento}:`, error); + } +}; + +// ================================================================================ +// Função Principal (com opção de pular a etapa do HubSoft) +// ================================================================================ + +const processaAtendimentos = async (skipHubSoft = false) => { + try { + let atendimentosDB = []; + + if (!skipHubSoft) { + // Esta parte depende da VPN + console.log('Buscando atendimentos do HubSoft...'); + atendimentosDB = await hubsoftModel.getAtendimentosFromDB(); + console.log(`Total de atendimentos obtidos do HubSoft: ${atendimentosDB.length}`); + + for (const atendimento of atendimentosDB) { + try { + // Processa o ticket do HubSoft + const ticketData = await processTicketFromHubSoft(atendimento); + // Salva no banco intermediário (hubglpi) + await saveTicketToHubGlpi(ticketData); + } + catch (error) { + console.error(`Erro ao processar atendimento ${atendimento.id_atendimento}:`, error); + } + } + + } else { + console.log('Pulando a busca de atendimentos do HubSoft (skipHubSoft = true).'); + } + + // Coletando atendimentos do banco intermediário (hubglpi) + atendimentosDB = await hubglpiModel.getTicketDataPending(); + + + for (const atendimento of atendimentosDB) { + try { + // Processa o atendimento para o GLPI + + console.log(atendimento.id_atendimento) + console.log(atendimento) + await processAtendimento(atendimento); + + } catch (error) { + console.error(`Erro ao processar atendimento ${atendimento.id_atendimento}:`, error); + } + } + + } catch (error) { + console.error('Erro ao processar atendimentos:', error); + } }; module.exports = { - processaAtendimentos -}; - + processaAtendimentos, + processAtendimento, + formatTicketDataForGlpi, + formatDescription +}; \ No newline at end of file diff --git a/model/glpiModel.js b/model/glpiModel.js new file mode 100644 index 0000000..50510ac --- /dev/null +++ b/model/glpiModel.js @@ -0,0 +1,77 @@ +// src/models/hubglpiModel.js +const dbConfig = require('../config/dbConfig.js'); +const { logError, logInfo} = require('../utils/logger'); + +const { Pool } = require('pg'); +const pool = new Pool({ + host: dbConfig.glpi.databaseHost, + port: dbConfig.glpi.databasePort, + database: dbConfig.glpi.databaseName, + user: dbConfig.glpi.databaseUser, + password: dbConfig.glpi.databasePassword +}); + + +class HubglpiModel { + + static async insertTicket(ticketData) { + + const query = ` + INSERT INTO glpi_tickets( + entities_id, + name, + date, + date_mod, + status, + user_id_recipient, + content, + urgency, + impact, + priority, + type, + itilcategories_id, + date_creation, + slas_id_ttr + ) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + RETURNING *; + `; + + const values = [ + ticketData.entidades_id, + ticketData.cliente_nome, + ticketData.data_cadastro, + ticketData.date_mod, + ticketData.status_atendimento, + ticketData.user_id_recipient, + ticketData.descricao_abertura, + ticketData.urgency, + ticketData.impact, + ticketData.priority, + ticketData.type, + ticketData.itilcategories_id, + ticketData.date_creation, + ]; + + + } + + static async selectEntityId(entity_name) { + + const query = `SELECT id FROM glpi_entities WHERE name = $1;`; + const values = [entity_name]; + + try { + const res = await pool.query + (query, values); + logInfo('Entidade encontrada:', res.rows[0]); + return res.rows[0]; + } catch (err) { + logError('Erro ao buscar entidade', err); + throw err; + } + + } +} + +module.exports = HubglpiModel; diff --git a/model/hubglpiModel.js b/model/hubglpiModel.js index 8ed19d4..1813667 100644 --- a/model/hubglpiModel.js +++ b/model/hubglpiModel.js @@ -1,4 +1,5 @@ -// src/models/hubglpiModel.js +// src/models/hubsoft_ticketsModel.js +const { log } = require('winston'); const dbConfig = require('../config/dbConfig.js'); const { logError, logInfo} = require('../utils/logger'); @@ -14,86 +15,84 @@ const pool = new Pool({ class HubglpiModel { - static async insertTicket(ticketData) { - const query = ` - INSERT INTO hubsoft_tickets ( + static async insertTicket(ticketData) { + const query = ` + INSERT INTO hubsoft_tickets ( id_atendimento, - codigo_cliente, - status_atendimento, + codigo_cliente, + status_atendimento, + codigo_servico, servico_nome, protocolo_hub, ticket_mundiale, cliente_nome, created_at - ) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8) - RETURNING *; - `; - - // Criando dicionario para traduzir status de atendimento - const statusAtendimentoMap = { - 1 : 'Pendente', - 2 : 'Em atendimento', - 3 : 'Resolvido', - 31 : 'Pendente', - 32 : 'Pendente', - 33 : 'Novo'}; - - // Substituindo o status do atendimento pelo valor correspondente - const status_atendimento = statusAtendimentoMap[ticketData.id_atendimento_status] || ticketData.id_atendimento_status; + ) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) + ON CONFLICT (id_atendimento) + DO UPDATE SET + codigo_cliente = $2, + status_atendimento = $3, + codigo_servico = $4, + servico_nome = $5, + protocolo_hub = $6, + ticket_mundiale = $7, + cliente_nome = $8, + created_at = $9 + RETURNING *; + `; - // Limpando todos os caracteres que não sao numeros e convertendo ticket_mundiale para int - ticketData.descricao_abertura = ticketData.descricao_abertura.replace(/[^0-9]/g, ''); - ticketData.ticket_mundiale = parseInt(ticketData.descricao_abertura) || null; + const values = [ + ticketData.id_atendimento, + ticketData.codigo_cliente, + ticketData.status_atendimento, + ticketData.codigo_servico, + ticketData.servico_nome, + ticketData.protocolo_hub, + ticketData.ticket_mundiale, + ticketData.cliente_nome, + ticketData.data_cadastro + ]; - ticketData.codigo_cliente = parseInt(ticketData.codigo_cliente) || null; + console.log('Executando query de inserção/atualização na tabela hubsoft_tickets:', query, values); - - const values = [ - ticketData.id_atendimento, - ticketData.codigo_cliente, - status_atendimento, - ticketData.descricao, - ticketData.protocolo_hub, - ticketData.ticket_mundiale, - ticketData.cliente_nome, - ticketData.data_cadastro - ]; - - console.log('Valores para inserção do ticket:', values); - - - try { - const res = await pool.query(query, values); - logInfo('Ticket Inserido na tabela hubsoft_tickets com sucesso:', res.rows[0]); - return res.rows[0]; - } catch (err) { - logError('Erro ao inserir ticket na tabela hubsoft_tickets', err); - throw err; - } - + try { + const res = await pool.query(query, values); + logInfo('Ticket Inserido/Atualizado na tabela hubsoft_tickets com sucesso:', res.rows[0]); + return res.rows[0]; + } catch (err) { + logError('Erro ao inserir/atualizar ticket na tabela hubsoft_tickets', err); + throw err; } +} + static async insertSyncData(syncData) { - const query = ` - INSERT INTO sync_data ( - hubsoft_ticket_id - )VALUES ($1) - RETURNING *; - `; - const values = [ - syncData - ]; - try { - const res = await pool.query(query, values); - logInfo('Dados inseridos na tabela sync_data com sucesso:', res.rows[0]); - return res.rows[0]; - } catch (err) { - logError('Erro ao inserir dados na tabela sync_data', err); - throw err; - } + + const query = ` + INSERT INTO sync_data ( + hubsoft_ticket_id + ) + VALUES ($1) + ON CONFLICT (hubsoft_ticket_id) + DO UPDATE SET + hubsoft_ticket_id = $1 + RETURNING *; + `; + const values = [ + syncData + ]; + + try { + const res = await pool.query(query, values); + logInfo('Dados inseridos/atualizados na tabela sync_data com sucesso:', res.rows[0]); + return res.rows[0]; + } catch (err) { + logError('Erro ao inserir/atualizar dados na tabela sync_data', err); + throw err; + } } static async update_syncData(sync_update) { @@ -165,9 +164,26 @@ class HubglpiModel { } } + + static async getTicketDataPending() { + const query = ` SELECT ht.id_atendimento, ht.codigo_cliente, ht.status_atendimento, ht.codigo_servico, ht.servico_nome, ht.protocolo_hub, ht.ticket_mundiale, ht.cliente_nome, ht.created_at, sd.id AS sync_data_id, sd.glpi_ticket_id, sd.status_sync, sd.sync_metadata, sd.last_sync_attempt, sd.sync_error_message, sd.created_at AS sync_created_at, sd.updated_at AS sync_updated_at + FROM hubsoft_tickets AS ht + LEFT JOIN sync_data AS sd ON ht.id_atendimento = sd.hubsoft_ticket_id + WHERE sd.status_sync IS NULL OR sd.status_sync = 'pending_create' + ORDER BY ht.created_at ASC; `; + + try { + const res = await pool.query(query); + logInfo('Tickets pendentes obtidos com sucesso:', res.rows); + return res.rows; + } catch (err) { + logError('Erro ao obter tickets pendentes', err); + throw err; + + } + + } } - - module.exports = HubglpiModel; diff --git a/model/hubsoftModel.js b/model/hubsoftModel.js index 42cbf27..6812bec 100644 --- a/model/hubsoftModel.js +++ b/model/hubsoftModel.js @@ -10,7 +10,7 @@ const pool = new Pool({ }); const getAtendimentosFromDB = async () => { - const query = 'SELECT a.id_atendimento, a.id_usuario_abertura, a.id_atendimento_status, a.protocolo, a.descricao_abertura, a.data_cadastro, a.nome_contato, c.codigo_cliente, s.descricao FROM atendimento AS a INNER JOIN cliente_servico AS cs ON a.id_cliente_servico = cs.id_cliente_servico INNER JOIN cliente AS c ON cs.id_cliente = c.id_cliente INNER JOIN servico AS s ON cs.id_servico = s.id_servico WHERE a.id_tipo_atendimento = 4 AND a.id_usuario_abertura = 248 AND a.id_atendimento_status IN (1, 2, 33) AND s.ativo = true;'; + const query = 'SELECT a.id_atendimento, a.id_usuario_abertura, a.id_atendimento_status, a.protocolo, a.descricao_abertura, a.data_cadastro, a.nome_contato, c.codigo_cliente, s.descricao, cs.id_cliente_servico FROM atendimento AS a INNER JOIN cliente_servico AS cs ON a.id_cliente_servico = cs.id_cliente_servico INNER JOIN cliente AS c ON cs.id_cliente = c.id_cliente INNER JOIN servico AS s ON cs.id_servico = s.id_servico WHERE a.id_tipo_atendimento = 4 AND a.id_usuario_abertura = 248 AND a.id_atendimento_status IN (1, 2, 33) AND s.ativo = true;'; const { rows } = await pool.query(query); return rows; diff --git a/model/sync_dataModel.js b/model/sync_dataModel.js deleted file mode 100644 index e69de29..0000000