diff --git a/src/controller/processController.js b/src/controller/processController.js
index 0d14cb9..4423ef2 100644
--- a/src/controller/processController.js
+++ b/src/controller/processController.js
@@ -62,8 +62,6 @@ const categoriaGLPI = {
'PABX IP - Até 30 ramais com telefones IPs (CAOA OUTROS ESTADOS)' : 5717 ,
'Link de Internet Dedicado 700MB Full Duplex' : 5707 ,
'Link de Internet Dedicado - Temporário' : 5707
-
-
}
@@ -121,7 +119,7 @@ const formatTicketDataForGlpi = async (ticketData) => {
type: 1,
itilcategories_id: categoriaGLPI[ticketData.servico_nome] || 1,
date_creation: new Date(),
- // entidades_id: 0 //await glpiModel.selectEntityId() //TODO: Implementar a busca da entidade
+ // entidades_id: 0 //await glpiModel.selectyId() //TODO: Implementar a busca da entidade
};
return formattedData;
};
diff --git a/src/model/hubglpiModel.js b/src/model/hubglpiModel.js
index 499795d..c3100b6 100644
--- a/src/model/hubglpiModel.js
+++ b/src/model/hubglpiModel.js
@@ -53,8 +53,7 @@ class HubglpiModel {
logError(`Erro ao inserir/atualizar ticket na tabela hubsoft_tickets: ${err}`);
throw err;
}
-}
-
+ }
static async insertSyncData(syncData) {
@@ -114,8 +113,7 @@ class HubglpiModel {
logError(`Erro ao atualizar dados na tabela sync_data: ${err}`);
throw err;
}
-}
-
+ }
static async get_idSyncByHubsoftId(hubsoft_ticket_id) {
const query = `
@@ -192,8 +190,9 @@ class HubglpiModel {
logError(`Erro ao obter glpi_ticket_id por id_atendimento, ${err}`);
throw err;
}
-}
-static async updateClosingTicket(syncId, closeMessage) {
+ }
+
+ static async updateClosingTicket(syncId, closeMessage) {
const client = await pool.connect();
try {
await client.query('BEGIN'); // inicia transação
@@ -238,10 +237,7 @@ static async updateClosingTicket(syncId, closeMessage) {
} finally {
client.release();
}
-}
-
-
-
+ }
static async updateSyncDataStatus( ticketId, status_sync, source_last){
const query = `
@@ -262,7 +258,6 @@ static async updateClosingTicket(syncId, closeMessage) {
}
}
-
static async updateSyncaDataError(sync_error_message, id_atendimento) {
const query = `
UPDATE sync_data
@@ -304,8 +299,6 @@ static async updateClosingTicket(syncId, closeMessage) {
throw err;
}
}
-
-
}
module.exports = HubglpiModel;
diff --git a/src/modules/createTickets/controller/createTickets.controller.js b/src/modules/createTickets/controller/createTickets.controller.js
new file mode 100644
index 0000000..de6a0f7
--- /dev/null
+++ b/src/modules/createTickets/controller/createTickets.controller.js
@@ -0,0 +1,46 @@
+// src/modules/createTickets/controller/createTickets.controller.js
+const mundialeService = require('../services/mundiale.service.js');
+const implantacaoService = require('../services/implantacao.service.js');
+const appMobileService = require('../services/appMobile.service.js');
+const ticketShared = require('../services/createTickets.service.js');
+
+const { logInfo } = require('../../utils/logger.js');
+
+async function processaAtendimentos() {
+ logInfo("[CONTROLLER] Iniciando processamento");
+
+ // 1️⃣ Buscar por fonte
+ const mundiale = await mundialeService.fetchNew();
+ const implantacao = await implantacaoService.fetchNew();
+ const app = await appMobileService.fetchNew();
+
+ // 2️⃣ Salvar no hubglpi
+ await mundialeService.saveHubGlpi(mundiale);
+ await implantacaoService.saveHubglpi(implantacao);
+ await appMobileService.saveHubglpi(app);
+
+ // 3️⃣ Buscar pendentes que foram salvos no banco hubglpi
+ const pendentes = await ticketShared.buscarPendentesHubglpi();
+
+ // 4️⃣ Roteamento por tipo
+ for (const ticket of pendentes) {
+
+ if (ticket.ticket_type === 'MUNDIALE') {
+ await mundialeService.sendToGlpi(ticket);
+
+ } else if (ticket.ticket_type === 'IMPLANTACAO') {
+ await implantacaoService.sendToGlpi(ticket);
+
+ } else if (ticket.ticket_type === 'APP_MOBILE') {
+ await appMobileService.sendToGlpi(ticket);
+
+ } else {
+ logInfo(`Tipo desconhecido, ignorando ticket id=${ticket.id}`);
+ continue;
+ }
+
+ await ticketShared.marcarComoProcessado(ticket.id);
+ }
+
+ logInfo("[CONTROLLER] Finalizado.");
+}
diff --git a/src/modules/createTickets/services/createTickets.service.js b/src/modules/createTickets/services/createTickets.service.js
new file mode 100644
index 0000000..0fbcadb
--- /dev/null
+++ b/src/modules/createTickets/services/createTickets.service.js
@@ -0,0 +1,44 @@
+// src/modules/createTickets/services/createTickets.service.js
+const repositoryHubGlpi = require('../../../shared/repositories/hubglpi.repository.js');
+
+// --------------------------------------
+// Funções principais do serviço
+// --------------------------------------
+
+async function fetcPendingTickets() {
+ return repositoryHubGlpi.fetchPendingTickets();
+}
+
+async function resolveEntityId(ticketData, glpiModel) {
+
+ const entityByService = await glpiModel.selectEntityIdCodServico(
+ ticketData.codigo_cliente,
+ ticketData.codigo_servico
+ );
+
+ if (entityByService) {
+ ticketData.entities_id = entityByService;
+ return ticketData;
+ }
+
+ // Tenta entidade pelo cliente
+ const entityByClient = await glpiModel.selectEntityIdCodCliente(
+ ticketData.codigo_cliente
+ );
+
+ if (entityByClient) {
+ ticketData.entities_id = entityByClient;
+ return ticketData;
+ }
+
+ // Fallback
+ ticketData.entities_id = 0;
+ return ticketData;
+}
+
+
+
+module.exports = {
+ fetcPendingTickets,
+ resolveEntityId
+}
\ No newline at end of file
diff --git a/src/modules/createTickets/services/implantacao.service.js b/src/modules/createTickets/services/implantacao.service.js
new file mode 100644
index 0000000..e69de29
diff --git a/src/modules/createTickets/services/mundiale.service.js b/src/modules/createTickets/services/mundiale.service.js
new file mode 100644
index 0000000..81d6b12
--- /dev/null
+++ b/src/modules/createTickets/services/mundiale.service.js
@@ -0,0 +1,115 @@
+// src/modules/createTickets/services/mundiale.service.js
+const repositoryHubGlpi = require('../../../shared/repositories/hubglpi.repository.js');
+const repositoryGlpi = require('../../../repositories/glpi.repository.js');
+const repositoryHubsoft = require('../../../shared/repositories/hubsoft.repository.js');
+const modelHubGlpi = require('../../../shared/model/hubglpi.model.js');
+const modelGlpi = require('../../../shared/model/glpi.model.js');
+const ticketShared = require('./createTickets.service.js');
+const { logInfo, logError, logWarning } = require('../../../utils/logger.js');
+
+// --------------------------------------
+// Funções principais do serviço
+// --------------------------------------
+
+async function fetchNew() {
+ return repositoryHubsoft.getMundialeTickets();
+}
+
+async function saveHubGlpi(tickets) {
+ if (!tickets.length) return;
+
+ const ticketsFormatted = tickets.map(ticket => modelHubGlpi.mapHubsoftToHubglpi(ticket, 'MUNDIALE'));
+
+ const inserted = await repositoryHubGlpi.insertTickets(ticketsFormatted);
+
+ if (inserted) {
+ await repositoryHubGlpi.insertSyncData(ticketsFormatted.map(ticket => ticket.id_atendimento));
+ }
+
+ return inserted;
+}
+
+async function sendToGlpi(ticket) {
+
+ const ticketsResolved = await ticketShared.resolveEntityId(ticket, modelGlpi);
+
+ const formattedTickets = formatGlpiPayload(ticketsResolved);
+
+ const payload = modelGlpi.mapHubGlpiToGlpi(formattedTickets);
+
+ const glpiId = await repositoryGlpi.insertTickets([payload]);
+
+ await repositoryHubGlpi.updateSyncDataCreated(ticket.id_atendimento, glpiId[0]);
+
+}
+
+
+// --------------------------------------
+// Formatar dados antes de enviar para o GLPI
+// --------------------------------------
+
+function formatGlpiPayload(ticket) {
+
+ const statusTexto = statusAtendimentoHubGlpi[ticket.status_atendimento] || 'Novo';
+ const statusGlpi = statusAtendimentoGLPI[statusTexto] || 1;
+
+ const categoria = categoriaGLPI[ticket.servico_nome] || 0;
+
+ const title = `[Mundiale] ${ticket.cliente_nome} - ${ticket.servico_nome} - ${ticket.ticket_mundiale}`;
+
+ const description = formatDescription(ticket);
+
+ return {
+ name: title,
+ content: description,
+ status: statusGlpi,
+ itilcategories_id: categoria,
+ created_at: ticket.created_at,
+ };
+}
+
+
+const formatDescription = (ticketData) => {
+
+ let htmlDescription = `
+
+
+ | Campo |
+ Valor |
+
+
+ | Nome: |
+ ${ticketData.cliente_nome} |
+
+
+ | Codigo: |
+ ${ticketData.codigo_cliente} |
+
+
+ | Serviço: |
+ ${ticketData.servico_nome} |
+
+
+ | Ticket Mundiale |
+ ${ticketData.ticket_mundiale} |
+
+
+ | Protocolo Hub: |
+ ${ticketData.protocolo_hub || 'N/A'} |
+
+
+ `;
+
+ return htmlDescription;
+};
+
+module.exports = {
+ fetchNew,
+ saveHubGlpi,
+ sendToGlpi
+}
+
+/**
+ * @module CreateTickets/MundialeService
+ * @description Serviço responsável por interagir com o Hubsoft e GLPI criação de tickets que provêm da Mundiale.
+ */
\ No newline at end of file
diff --git a/src/modules/createTickets/services/sac.service.js b/src/modules/createTickets/services/sac.service.js
new file mode 100644
index 0000000..e69de29
diff --git a/src/scripts/data/database.sql b/src/scripts/data/database.sql
index 4c249b6..795b68d 100644
--- a/src/scripts/data/database.sql
+++ b/src/scripts/data/database.sql
@@ -85,4 +85,11 @@ CREATE TABLE sync_control (
-- Inserir o registro inicial para o nosso novo cron de comentários
INSERT INTO sync_control (job_name, last_run_timestamp) VALUES ('hubsoft_comments_sync', '2024-01-01 00:00:00');
+-- =============================================
+-- ALTERAÇÕES NA TABELA hubsoft_tickets
+-- =============================================
+ALTER TABLE hubsoft_tickets
+ADD COLUMN ticket_type VARCHAR(50) NOT NULL DEFAULT 'MUNDIALE';
+ALTER TABLE hubsoft_tickets
+ADD CONSTRAINT hubsoft_id_atendimento_unique UNIQUE (id_atendimento);
diff --git a/src/shared/model/glpi.model.js b/src/shared/model/glpi.model.js
new file mode 100644
index 0000000..01db487
--- /dev/null
+++ b/src/shared/model/glpi.model.js
@@ -0,0 +1,37 @@
+// src/shared/model/glpi.model.js
+
+function mapHubGlpiToGlpi(ticket) {
+ return {
+ entities_id: ticket.entities_id || 0,
+ name: ticket.name,
+ date: ticket.created_at,
+ date_mod: Now(),
+ status: defineStatusGlpi(ticket.status_atendimento),
+ users_id_recipient: process.env.GLPI_USER_ID || 0,
+ content: ticket.content,
+ urgency: 3,
+ impact: 3,
+ priority: 3,
+ type: 2,
+ date_creation : ticket.date_creation || Now(),
+ slas_id_ttr: 37
+ };
+}
+
+function defineStatusGlpi(Status) {
+ const mapStatusDBtoGLPI = {
+ 'Novo': 1,
+ 'Pendente': 4,
+ 'Em atendimento': 2,
+ 'Resolvido': 5
+ };
+
+ return mapStatusDBtoGLPI[Status] || 'Novo';
+};
+
+
+
+module.exports = {
+ mapHubGlpiToGlpi,
+ defineStatusGlpi
+};
diff --git a/src/shared/model/hubglpi.model.js b/src/shared/model/hubglpi.model.js
new file mode 100644
index 0000000..a3bba71
--- /dev/null
+++ b/src/shared/model/hubglpi.model.js
@@ -0,0 +1,34 @@
+// src/shared/model/hubglpi.model.js
+
+function mapHubsoftToHubglpi(ticket, type) {
+ return {
+ id_atendimento: ticket.id_atendimento,
+ codigo_cliente: ticket.codigo_cliente,
+ status_atendimento: defineStatusHubglpi(ticket.id_atendimento_status),
+ servico_nome: ticket.descricao,
+ protocolo_hub: ticket.protocolo,
+ ticket_mundiale: parseInt(ticket.descricao_abertura.replace(/[^0-9]/g, ''))|| null,
+ codigo_servico: ticket.id_cliente_servico,
+ cliente_nome: ticket.nome_contato,
+ descricao_fechamento: ticket.descricao_fechamento || null,
+ data_fechamento: ticket.data_fechamento || null,
+ created_at: ticket.data_cadastro,
+ ticket_type: type
+ };
+}
+
+function defineStatusHubglpi(hubsoftStatus) {
+ const statusMap = {
+ 1: 'Pendente',
+ 2: 'Em atendimento',
+ 3: 'Resolvido',
+ 31: 'Pendente',
+ 32: 'Pendente',
+ 33: 'Novo'
+ }
+ return statusMap[hubsoftStatus] || 'Novo';
+};
+
+module.exports = {
+ mapHubsoftToHubglpi
+};
diff --git a/src/shared/repositories/glpi.repository.js b/src/shared/repositories/glpi.repository.js
new file mode 100644
index 0000000..627659c
--- /dev/null
+++ b/src/shared/repositories/glpi.repository.js
@@ -0,0 +1,40 @@
+// src/shared/repositories/glpi.repository.js
+const db = require('../../../config/database.js');
+const { logInfo, logError } = require('../../utils/logger.js');
+
+async function insertTickets(tickets) {
+
+ const values = tickets.map(t => [
+ t.status,
+ t.content,
+ t.users_id_recipient,
+ t.entities_id,
+ t.type,
+ t.requesttypes_id,
+ t.urgency,
+ t.impact,
+ t.priority,
+ t.date,
+ t.name
+ ]);
+
+ const placeholders = values
+ .map(() => "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
+ .join(",");
+
+ const sql = `
+ INSERT INTO glpi_tickets
+ (status, content, users_id_recipient, entities_id, type, requesttypes_id,
+ urgency, impact, priority, date, name)
+ VALUES ${placeholders}
+ `;
+
+ const result = await mariadb.execute(sql, values.flat());
+
+ return result.insertId ? [result.insertId] : [];
+}
+
+
+module.exports = {
+ insertTickets
+};
\ No newline at end of file
diff --git a/src/shared/repositories/hubglpi.repository.js b/src/shared/repositories/hubglpi.repository.js
new file mode 100644
index 0000000..1525019
--- /dev/null
+++ b/src/shared/repositories/hubglpi.repository.js
@@ -0,0 +1,132 @@
+// src/shared/repositories/hubglpi.repository.js
+const { get } = require('pm2');
+const db = require('../../../config/database.js');
+const { logInfo, logError } = require('../../utils/logger.js');
+const { query } = require('winston');
+const { hubsoft } = require('../../config/dbConfig.js');
+
+async function insertTickets(tickets) {
+ try {
+ const values = tickets.map(t => [
+ t.id_atendimento,
+ t.codigo_cliente,
+ t.status_atendimento,
+ t.servico_nome,
+ t.protocolo_hub,
+ t.ticket_mundiale,
+ t.codigo_servico,
+ t.cliente_nome,
+ t.descricao_fechamento,
+ t.data_fechamento,
+ t.created_at,
+ t.updated_at,
+ t.ticket_type
+ ]);
+
+ const rowPlaceholders = values
+ .map(
+ (_, i) =>
+ `(${Array(13)
+ .fill(0)
+ .map((_, j) => `$${i * 13 + j + 1}`)
+ .join(", ")})`
+ )
+ .join(", ");
+
+ const query = `
+ INSERT INTO hubsoft_tickets
+ (id_atendimento, codigo_cliente, status_atendimento, servico_nome, protocolo_hub, ticket_mundiale,
+ codigo_servico, cliente_nome, descricao_fechamento, data_fechamento, created_at, updated_at, type)
+ VALUES ${rowPlaceholders}
+ ON CONFLICT (id_atendimento)
+ DO UPDATE SET
+ codigo_cliente = EXCLUDED.codigo_cliente,
+ status_atendimento = EXCLUDED.status_atendimento,
+ servico_nome = EXCLUDED.servico_nome,
+ protocolo_hub = EXCLUDED.protocolo_hub,
+ ticket_mundiale = EXCLUDED.ticket_mundiale,
+ codigo_servico = EXCLUDED.codigo_servico,
+ cliente_nome = EXCLUDED.cliente_nome,
+ descricao_fechamento = EXCLUDED.descricao_fechamento,
+ data_fechamento = EXCLUDED.data_fechamento,
+ updated_at = EXCLUDED.updated_at,
+ type = EXCLUDED.type
+ `;
+
+ const flattened = values.flat();
+
+ await db.query(query, flattened);
+
+ return { success: true, ids: tickets.map(t => t.id_atendimento) };
+
+ } catch (error) {
+ logError("Erro ao inserir tickets no HubGLPI:", error);
+ throw error;
+ }
+}
+
+async function insertSyncData(ids) {
+ try {
+ const values = ids.map(id => [id]);
+
+ const rowPlaceholders = values
+ .map((_, i) => `($${i + 1})`)
+ .join(", ");
+
+ const query = `
+ INSERT INTO hubsoft_sync_data (hubsoft_ticket_id)
+ VALUES ${rowPlaceholders}
+ ON CONFLICT (hubsoft_ticket_id) DO UPDATE SET
+ hubsoft_ticket_id = $1
+ `;
+
+ const flattened = ids;
+
+ await db.query(query, flattened);
+
+ return true;
+ }
+ catch (error) {
+ logError("Erro ao inserir dados de sincronização no HubGLPI:", error);
+ throw error;
+ }
+}
+
+async function fetchPendingTickets() {
+ try {
+ 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, ht.ticket_type as ticket_type
+ 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;`
+ const result = await db.query(query);
+ return result.rows
+ } catch (error) {
+ logError("Erro ao buscar tickets pendentes no HubGLPI:", error);
+ throw error;
+ }
+}
+
+async function updateSyncDataCreated(hubsoftTicketId, glpiId) {
+ const sql = `
+ UPDATE sync_data
+ SET
+ status_sync = 'created_glpi',
+ updated_at = NOW(),
+ last_sync_attempt = NOW(),
+ glpi_ticket_id = ?
+ WHERE hubsoft_ticket_id = ?
+ `;
+
+ await db.query(sql, [glpiId, hubsoftTicketId]);
+}
+
+
+
+
+module.exports = {
+ insertTickets,
+ insertSyncData,
+ fetchPendingTickets,
+ updateSyncDataCreated
+};
\ No newline at end of file
diff --git a/src/shared/repositories/hubsoft.repository.js b/src/shared/repositories/hubsoft.repository.js
new file mode 100644
index 0000000..ab2276e
--- /dev/null
+++ b/src/shared/repositories/hubsoft.repository.js
@@ -0,0 +1,60 @@
+// src/shared/repositories/hubsoft.repository.js
+const { get } = require('pm2');
+const db = require('../../../config/database.js');
+const { logInfo, logError } = require('../../utils/logger.js');
+
+
+async function getMundialeTickets() {
+
+ try {
+ 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 db.query(query);
+ return rows;
+ } catch (error) {
+ logError("Erro ao buscar tickets Mundiale:", error);
+ throw error;
+ }
+}
+
+async function getImplantacaoTickets() {
+ try {
+ 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 = 21 AND a.id_atendimento_status IN (1, 2, 33) AND s.ativo = true;`;
+ const { rows } = await db.query(query);
+ return rows;
+
+ } catch (error) {
+ logError("Erro ao buscar tickets Implantação:", error);
+ throw error;
+ }
+}
+
+async function getCancelamentoTickets() {
+ try {
+ 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 = 27 AND a.id_atendimento_status IN (1, 2, 33) AND s.ativo = true;`;
+ const { rows } = await db.query(query);
+ return rows;
+ } catch (error) {
+ logError("Erro ao buscar tickets Cancelamento:", error);
+ throw error;
+ }
+}
+
+async function getSacTickets() {
+ try {
+ 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 = 41 AND a.id_atendimento_status IN (1, 2, 33) AND s.ativo = true;`;
+ const { rows } = await db.query(query);
+ return rows;
+ } catch (error) {
+ logError("Erro ao buscar tickets SAC:", error);
+ throw error;
+ }
+}
+
+
+module.exports = {
+ getMundialeTickets,
+ getImplantacaoTickets,
+ getCancelamentoTickets,
+ getSacTickets
+
+};
\ No newline at end of file