diff --git a/src/modules/createTickets/controller/createTickets.controller.js b/src/modules/createTickets/controller/createTickets.controller.js
new file mode 100644
index 0000000..449bf30
--- /dev/null
+++ b/src/modules/createTickets/controller/createTickets.controller.js
@@ -0,0 +1,55 @@
+// src/modules/createTickets/controller/createTickets.controller.js
+const mundialeService = require('../services/mundiale.service.js');
+const implantacaoService = require('../services/implantacao.service.js');
+const sacService = require('../services/sac.service.js');
+const ticketShared = require('../services/createTickets.service.js');
+
+const { logInfo } = require('../../../utils/logger.js');
+
+async function processAtendimentos() {
+ logInfo("[CONTROLLER] Iniciando processamento");
+
+ // 1️⃣ Buscar por fonte
+ const mundiale = await mundialeService.fetchNew();
+ logInfo(`Encontrados ${mundiale.length} tickets Mundiale para processar.`);
+ const implantacao = await implantacaoService.fetchNew();
+ logInfo(`Encontrados ${implantacao.length} tickets de Implantação para processar.`);
+ //const sac = await sacService.fetchNew();
+
+ // 2️⃣ Salvar no hubglpi
+ await mundialeService.saveHubGlpi(mundiale);
+ await implantacaoService.saveHubGlpi(implantacao);
+ //await sacService.saveHubGlpi(sac);
+
+ // 3️⃣ Buscar pendentes que foram salvos no banco hubglpi
+ const pendentes = await ticketShared.fetchPendingTickets();
+
+ // 4️⃣ Roteamento por tipo
+ for (const ticket of pendentes) {
+
+ let glpiId;
+
+ if (ticket.ticket_type === 'MUNDIALE') {
+ glpiId = await mundialeService.sendToGlpi(ticket);
+
+ } else if (ticket.ticket_type === 'IMPLANTACAO') {
+ glpiId = await implantacaoService.sendToGlpi(ticket);
+
+ } else if (ticket.ticket_type === 'SAC') {
+ glpiId = await sacService.sendToGlpi(ticket);
+
+ } else {
+ logInfo(`Tipo desconhecido, ignorando ticket id=${ticket.id}`);
+ continue;
+ }
+
+ ticketShared.insertCreateComment(ticket.id_atendimento, glpiId);
+
+
+ }
+ logInfo("[CONTROLLER] Finalizado.");
+}
+
+module.exports = { processAtendimentos };
+
+
diff --git a/src/modules/createTickets/services/createTickets.service.js b/src/modules/createTickets/services/createTickets.service.js
new file mode 100644
index 0000000..e7715b1
--- /dev/null
+++ b/src/modules/createTickets/services/createTickets.service.js
@@ -0,0 +1,73 @@
+// src/modules/createTickets/services/createTickets.service.js
+const repositoryHubGlpi = require('../../../shared/repositories/hubglpi.repository.js');
+const repositoryGlpi = require('../../../shared/repositories/glpi.repository.js');
+const repositoryHubSoft = require('../../../shared/repositories/hubsoftAPI.repository.js');
+const modelHubsoft = require('../../../shared/model/hubsoft.model.js');
+const { hubglpi } = require('../../../config/dbConfig.js');
+
+// --------------------------------------
+// Funções principais do serviço
+// --------------------------------------
+
+async function fetchPendingTickets() {
+ return repositoryHubGlpi.fetchPendingTickets();
+}
+
+async function resolveEntityId(ticketData) {
+
+ const entityByService = await repositoryGlpi.getEntitiesByService(
+ ticketData.codigo_cliente,
+ ticketData.codigo_servico
+ );
+
+ if (entityByService) {
+ ticketData.entities_id = entityByService;
+ return ticketData;
+ }
+
+ const entityByClient = await repositoryGlpi.getEntitiesByClient(
+ ticketData.codigo_cliente
+ );
+
+ if (entityByClient) {
+ ticketData.entities_id = entityByClient;
+ return ticketData;
+ }
+
+ ticketData.entities_id = 0;
+ return ticketData;
+}
+
+// Funcao para definir ticket como processado
+async function setAsCreated(dataTicket) {
+
+}
+
+
+//Cria uma nova entidade no GLPI
+async function createEntity(ticketData) {
+
+ const entity_name = `${ticketData.codigo_cliente} - ${ticketData.nome_razao_social}`;
+
+ await repositoryGlpi.insertEntity(entity_name);
+
+ return;
+}
+
+async function insertCreateComment(ticketData, glpiId) {
+
+ const message = `Atendimento ${glpiId} criado com sucesso`;
+
+ await repositoryHubSoft.sendMessage(ticketData.id_atendimento, message);
+
+ await repositoryHubGlpi.sendMessage(glpiId, message);
+
+ return;
+}
+
+module.exports = {
+ fetchPendingTickets,
+ resolveEntityId,
+ setAsCreated,
+ createEntity
+}
\ 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..0b85e62
--- /dev/null
+++ b/src/modules/createTickets/services/implantacao.service.js
@@ -0,0 +1,254 @@
+// src/modules/createTickets/services/mundiale.service.js
+const repositoryHubGlpi = require('../../../shared/repositories/hubglpi.repository.js');
+const repositoryGlpi = require('../../../shared/repositories/glpi.repository.js');
+const repositoryHubsoft = require('../../../shared/repositories/hubsoftDB.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, logWarning } = require('../../../utils/logger.js');
+
+// --------------------------------------
+// Funções principais do serviço
+// --------------------------------------
+
+async function fetchNew() {
+ return repositoryHubsoft.getImplantacaoTickets();
+}
+
+async function saveHubGlpi(tickets) {
+ if (!tickets.length) return;
+
+ const ticketsFormatted = tickets.map(ticket =>
+ modelHubGlpi.mapHubsoftToHubglpi(ticket, 'IMPLANTACAO')
+ );
+
+ 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);
+
+ if (ticketsResolved.entities_id === 0) {
+ logWarning(`Entidade não encontrada para o ticket id=${ticket.id_atendimento}.`);
+ }
+
+ formatGlpiPayload(ticketsResolved);
+
+ const payload = modelGlpi.mapHubGlpiToGlpi(ticketsResolved);
+
+ const glpiId = await repositoryGlpi.insertTicket(payload);
+ logInfo(`Ticket GLPI criado com ID=${glpiId} para o ticket id=${ticket.id_atendimento}.`);
+
+ await repositoryGlpi.insertGroupTicket(glpiId, 'IMPLANTACAO');
+ await repositoryHubGlpi.updateSyncDataCreated(ticket.id_atendimento, glpiId);
+}
+
+
+// --------------------------------------
+// Formatar dados antes de enviar para o GLPI
+// --------------------------------------
+
+function formatGlpiPayload(ticket) {
+ const title = `IMPLANTAÇÃO - ${ticket.codigo_cliente}-${ticket.codigo_servico}-${ticket.nome_razaosocial} - ${ticket.servico_nome}`;
+ const description = formatDescription(ticket);
+ ticket.name = title;
+ ticket.content = description;
+ return;
+}
+
+
+// --------------------------------------
+// Dicionário de Serviços
+// --------------------------------------
+
+const serviceDictionary = {
+ // ---------- LAN-TO-LAN ----------
+ "Lan-to-Lan 50 Mbps": { produto: "Lan-to-Lan", qtd: 1, descricao: "50 Mbps" },
+ "Lan-to-Lan 100 Mbps": { produto: "Lan-to-Lan", qtd: 1, descricao: "100 Mbps" },
+ "Lan-to-Lan 200 Mbps": { produto: "Lan-to-Lan", qtd: 1, descricao: "200 Mbps" },
+ "Lan-to-Lan 300 Mbps": { produto: "Lan-to-Lan", qtd: 1, descricao: "300 Mbps" },
+ "Lan-to-Lan 500 Mbps": { produto: "Lan-to-Lan", qtd: 1, descricao: "500 Mbps" },
+ "Lan-to-Lan 700 Mbps": { produto: "Lan-to-Lan", qtd: 1, descricao: "700 Mbps" },
+ "Lan-to-Lan": { produto: "Lan-to-Lan", qtd: 1, descricao: null },
+
+ // ---------- LINK DEDICADO ----------
+ "Link de Internet Dedicado 20 Mbps Full Duplex":
+ { produto: "Link de Internet Dedicado", qtd: 1, descricao: "20 Mbps Full Duplex" },
+
+ "Link de Internet Dedicado 100 Mbps Full Duplex":
+ { produto: "Link de Internet Dedicado", qtd: 1, descricao: "100 Mbps Full Duplex" },
+
+ "Link de Internet Dedicado 1Gbps Full Duplex":
+ { produto: "Link de Internet Dedicado", qtd: 1, descricao: "1 Gbps Full Duplex" },
+
+ "Link de Internet Dedicado 2 Gbps Full Duplex":
+ { produto: "Link de Internet Dedicado", qtd: 1, descricao: "2 Gbps Full Duplex" },
+
+ // Default genérico caso venha coisa nova
+};
+
+
+// --------------------------------------
+// Tabela HTML da descrição
+// --------------------------------------
+
+const formatDescription = (ticket) => {
+
+ const documentoFormatado = formatDocument(ticket.cpf_cnpj, ticket.tipo_pessoa);
+ const docLabel = ticket.tipo_pessoa === "pf" ? "CPF" : "CNPJ";
+ const servico = resolveService(ticket.servico_nome);
+
+ return `
+
+
+
+
+ |
+ Dados Comercial
+ |
+
+
+
+ | Nº de Operação |
+ ${ticket.protocolo_hub || "N/A"} |
+
+
+
+ | Gerente Responsável |
+ ${ticket.usuario_que_abriu || "N/A"} |
+
+
+
+ | Código do Cliente |
+ ${ticket.codigo_cliente} |
+
+
+
+
+ |
+ Dados Cliente
+ |
+
+
+
+ | Cliente |
+ ${ticket.nome_razaosocial} |
+
+
+
+ | ${docLabel} |
+ ${documentoFormatado} |
+
+
+
+ | Nome Contato |
+ ${ticket.cliente_nome || "N/A"} |
+
+
+
+ | Email Contato |
+ ${ticket.email || "N/A"} |
+
+
+
+ | Telefone Contato |
+ ${ticket.telefone || "N/A"} |
+
+
+
+ | Endereço Instalação |
+ ${ticket.endereco || "N/A"} |
+
+
+
+
+ |
+ Dados do Serviço Contratado
+ |
+
+
+
+
+
+
+ | Produto |
+ Quantidade |
+ Descrição |
+
+
+ | ${servico.produto} |
+ ${servico.qtd} |
+ ${servico.descricao || ""} |
+
+
+ |
+
+
+
+
+ |
+ Observações
+ |
+
+
+
+ | ${ticket.descricao_abertura || "N/A"} |
+
+
+
+ `;
+};
+
+
+
+// --------------------------------------
+// Formatadores de Documento
+// --------------------------------------
+
+function formatCPF(cpf) {
+ cpf = cpf.replace(/\D/g, "");
+ return cpf.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, "$1.$2.$3-$4");
+}
+
+function formatCNPJ(cnpj) {
+ cnpj = cnpj.replace(/\D/g, "");
+ return cnpj.replace(/(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})/, "$1.$2.$3/$4-$5");
+}
+
+function formatDocument(doc, tipo) {
+ if (!doc) return "N/A";
+ return tipo === "pf" ? formatCPF(doc) : formatCNPJ(doc);
+}
+
+
+// --------------------------------------
+// Resolve Serviço
+// --------------------------------------
+
+function resolveService(servicoNome) {
+ return serviceDictionary[servicoNome] || {
+ produto: servicoNome,
+ qtd: 1,
+ descricao: null
+ };
+}
+
+
+// --------------------------------------
+// Exportação
+// --------------------------------------
+
+module.exports = {
+ fetchNew,
+ saveHubGlpi,
+ sendToGlpi
+};
diff --git a/src/modules/createTickets/services/mundiale.service.js b/src/modules/createTickets/services/mundiale.service.js
new file mode 100644
index 0000000..051550a
--- /dev/null
+++ b/src/modules/createTickets/services/mundiale.service.js
@@ -0,0 +1,128 @@
+// src/modules/createTickets/services/mundiale.service.js
+const repositoryHubGlpi = require('../../../shared/repositories/hubglpi.repository.js');
+const repositoryGlpi = require('../../../shared/repositories/glpi.repository.js');
+const repositoryHubsoft = require('../../../shared/repositories/hubsoftDB.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');
+const { log } = require('winston');
+
+// --------------------------------------
+// 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);
+
+ formatGlpiPayload(ticketsResolved);
+
+ const payload = modelGlpi.mapHubGlpiToGlpi(ticketsResolved);
+
+ const glpiId = await repositoryGlpi.insertTicket(payload);
+ logInfo(`Ticket GLPI criado com ID: ${glpiId} para ticket Hubsoft ID: ${ticket.id_atendimento}`);
+
+ await repositoryGlpi.insertGroupTicket(glpiId, 'MUNDIALE');
+ logInfo(`Grupo padrão atribuído ao ticket GLPI ID: ${glpiId}`);
+
+ await repositoryHubGlpi.updateSyncDataCreated(ticket.id_atendimento, glpiId);
+ logInfo(`Ticket HubGlpi ID: ${ticket.id_atendimento} marcado como criado no Banco intermediario com ID: ${glpiId}`);
+
+ return glpiId;
+
+}
+
+// --------------------------------------
+// Mapeamento e formatação de dados
+// --------------------------------------
+
+
+const statusAtendimentoGLPI = {
+ 'Novo': 1,
+ 'Pendente': 4,
+ 'Em atendimento': 2,
+ 'Resolvido': 5
+};
+
+
+
+// --------------------------------------
+// Formatar dados antes de enviar para o GLPI
+// --------------------------------------
+
+function formatGlpiPayload(ticket) {
+
+ ticket.status = statusAtendimentoGLPI[ticket.status_atendimento] || 1;
+
+ ticket.name = `Mundiale - Protocolo: ${ticket.ticket_mundiale} - ${ticket.cliente_nome}`;
+
+ ticket.content = formatDescription(ticket);
+
+
+ return;
+}
+
+
+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/shared/infra/api/hubsoft.auth.js b/src/shared/infra/api/hubsoft.auth.js
new file mode 100644
index 0000000..7fe1512
--- /dev/null
+++ b/src/shared/infra/api/hubsoft.auth.js
@@ -0,0 +1,20 @@
+// src/shared/infra/api/hubsoft.auth.js
+
+const getAuthToken = async () => {
+ try {
+ const response = await axios.post(
+ apiConfig.hubsoft.authUrl,
+ qs.stringify(apiConfig.hubsoft.authPayload),
+ {
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ }
+ }
+ );
+
+ return response.data.access_token;
+ } catch (error) {
+ logError('Erro ao obter token de autenticação:', error);
+ throw error;
+ }
+};
diff --git a/src/shared/infra/api/hubsoft.config.js b/src/shared/infra/api/hubsoft.config.js
new file mode 100644
index 0000000..d1581a6
--- /dev/null
+++ b/src/shared/infra/api/hubsoft.config.js
@@ -0,0 +1,19 @@
+// src/shared/infra/api/hubsoft.config.js
+
+module.exports = {
+ hubsoft: {
+ baseUrl: process.env.HUBSOFT_BASE_URL,
+
+ authUrl: `${process.env.HUBSOFT_BASE_URL}/oauth/token`,
+
+ authPayload: {
+ grant_type: 'password',
+ client_id: process.env.HUBSOFT_CLIENT_ID,
+ client_secret: process.env.HUBSOFT_CLIENT_SECRET,
+ username: process.env.HUBSOFT_USER,
+ password: process.env.HUBSOFT_PASS
+ },
+
+ atendimentosUrl: `${process.env.HUBSOFT_BASE_URL}/atendimentos/`
+ }
+};
\ No newline at end of file
diff --git a/src/shared/model/hubsoft.model.js b/src/shared/model/hubsoft.model.js
new file mode 100644
index 0000000..e69de29
diff --git a/src/shared/repositories/hubsoftAPI.repository.js b/src/shared/repositories/hubsoftAPI.repository.js
new file mode 100644
index 0000000..1b61c92
--- /dev/null
+++ b/src/shared/repositories/hubsoftAPI.repository.js
@@ -0,0 +1,38 @@
+// src/shared/repositories/hubsoftAPI.repository.js
+
+const axios = require('axios');
+const {getAuthToken} = require('../infra/api/hubsoft.auth.js');
+const apiConfig = require('../infra/api/hubsoft.config');
+const { logError, logInfo } = require('../../utils/logger');
+
+async function sendMessage(idAtendimento, mensagem) {
+ try {
+ const token = await getAuthToken();
+
+ const url = `${apiConfig.hubsoft.atendimentosUrl}adicionar_mensagem/${idAtendimento}`;
+
+ logInfo(`[HubSoft API] Enviando comentário para atendimento ${idAtendimento}`);
+
+ await axios.post(
+ url,
+ { mensagem },
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ 'Content-Type': 'application/json'
+ }
+ }
+ );
+
+ } catch (error) {
+ logError(
+ `[HubSoft API] Erro ao enviar comentário`,
+ error.response?.data || error.message
+ );
+ throw error;
+ }
+}
+
+module.exports = {
+ sendMessage
+};
diff --git a/src/shared/repositories/hubsoftDB.repository.js b/src/shared/repositories/hubsoftDB.repository.js
new file mode 100644
index 0000000..524c967
--- /dev/null
+++ b/src/shared/repositories/hubsoftDB.repository.js
@@ -0,0 +1,118 @@
+// src/shared/repositories/hubsoft.repository.js
+const { get } = require('pm2');
+const { hubsoft } = require("../infra/database/index.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 hubsoft.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,
+
+ -- DADOS DO CLIENTE (novos)
+ c.nome_razaosocial,
+ c.telefone_primario AS telefone,
+ c.cpf_cnpj,
+ c.tipo_pessoa,
+ c.email_principal AS email,
+
+ c.codigo_cliente,
+ s.descricao AS descricao,
+ cs.id_cliente_servico,
+
+ -- ENDEREÇO DE INSTALAÇÃO
+ en.endereco,
+ en.numero,
+ en.complemento,
+ en.bairro,
+ en.cep,
+ ci.nome AS cidade,
+ es.sigla AS estado
+
+ 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
+
+ -- endereço do cliente
+ INNER JOIN cliente_servico_endereco AS cse
+ ON cse.id_cliente_servico = cs.id_cliente_servico
+ AND cse.tipo = 'instalacao'
+
+ INNER JOIN endereco_numero AS en
+ ON en.id_endereco_numero = cse.id_endereco_numero
+
+ INNER JOIN cidade AS ci
+ ON ci.id_cidade = en.id_cidade
+
+ INNER JOIN estado AS es
+ ON es.id_estado = ci.id_estado
+
+ WHERE
+ a.id_tipo_atendimento = 21
+ AND a.id_atendimento_status IN (1, 2, 33)
+ AND s.ativo = TRUE;
+ `;
+ const { rows } = await hubsoft.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 hubsoft.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 hubsoft.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