// src/models/ticketSnModel.js const pool = require('../data/database'); const { logInfo, logError, logWarning } = require('../utils/logger'); class TicketSnModel { // Salva ou atualiza um ticket no banco de dados static async saveTicket(ticketData, typeTicket) { const query = ` INSERT INTO tickets_sn (ticket_number, sys_id, short_description, status, description, caller_id, caller_email, location_id, location_name, ramal, telefone, opened_at, tipo) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) ON CONFLICT (ticket_number) DO UPDATE SET sys_id = EXCLUDED.sys_id, short_description = EXCLUDED.short_description, status = EXCLUDED.status, description = EXCLUDED.description, caller_id = EXCLUDED.caller_id, caller_email = EXCLUDED.caller_email, location_id = EXCLUDED.location_id, location_name = EXCLUDED.location_name, ramal = EXCLUDED.ramal, telefone = EXCLUDED.telefone, opened_at = EXCLUDED.opened_at, tipo = EXCLUDED.tipo, updated_at = NOW() RETURNING id, xmax = 0 as inserted`; const values = [ ticketData.number?.value || null, ticketData.sys_id?.value || null, ticketData.short_description?.value || null, ticketData.state?.display_value || null, ticketData.description?.value || null, ticketData.opened_by?.display_value || null, ticketData.sys_created_by?.display_value || null, ticketData.location?.value || null, ticketData.location?.display_value || null, ticketData['variables.numero_ramal']?.value || null, ticketData['variables.telephone_favorecido_rh']?.value || null, ticketData.opened_at?.value ? new Date(ticketData.opened_at.value) : null, typeTicket ]; try { const result = await pool.query(query, values); const wasInserted = result.rows[0].inserted; if (wasInserted) { logInfo(`✅ NOVO ticket salvo! ID: ${result.rows[0].id}`, { ticket_id: result.rows[0].id, ticket_number: values[0], action: 'insert' }); } else { logInfo(`🔄 Ticket atualizado! ID: ${result.rows[0].id}`, { ticket_id: result.rows[0].id, ticket_number: values[0], action: 'update' }); } return result.rows[0].id; } catch (error) { logError(error, `❌ Erro ao salvar ticket ${values[0]}`); throw error; } } // Busca tickets que ainda não foram sincronizados com o GLPI static async getPendingTickets() { try { const query = ` SELECT ts.id, ts.ticket_number, ts.short_description, ts.description, ts.status, ts.caller_id, ts.caller_email, ts.location_id, ts.location_name, ts.ramal, ts.telefone, ts.opened_at, ts.tipo, ts.created_at, ts.updated_at FROM tickets_sn ts LEFT JOIN ticket_sync tsync ON ts.id = tsync.sn_ticket_id WHERE tsync.id IS NULL AND ts.status NOT IN ('Encerrado', 'Encerrado - Omitido') ORDER BY ts.opened_at `; const result = await pool.query(query); logInfo(`📋 Tickets pendentes encontrados: ${result.rows.length}`); return result.rows; } catch (error) { logError(error, '❌ Erro ao buscar tickets pendentes'); return []; } } // Busca um ticket pelo número static async findByTicketNumber(ticketNumber) { try { const query = ` SELECT * FROM tickets_sn WHERE ticket_number = $1 `; const result = await pool.query(query, [ticketNumber]); return result.rows[0] || null; } catch (error) { logError(error, `❌ Erro ao buscar ticket por número: ${ticketNumber}`); return null; } } // Busca um ticket pelo ID static async findById(ticketId) { try { const query = ` SELECT * FROM tickets_sn WHERE id = $1 `; const result = await pool.query(query, [ticketId]); return result.rows[0] || null; } catch (error) { logError(error, `❌ Erro ao buscar ticket por ID: ${ticketId}`); return null; } } // Atualiza os dados de um ticket static async updateTicket(ticketId, updateData) { try { const fields = []; const values = []; let paramCount = 1; // Construir dinamicamente a query baseada nos campos fornecidos for (const [key, value] of Object.entries(updateData)) { fields.push(`${key} = $${paramCount}`); values.push(value); paramCount++; } // Adicionar updated_at fields.push(`updated_at = NOW()`); // Adicionar o ID no final dos valores values.push(ticketId); const query = ` UPDATE tickets_sn SET ${fields.join(', ')} WHERE id = $${paramCount} RETURNING * `; const result = await pool.query(query, values); if (result.rows.length > 0) { logInfo(`✅ Ticket atualizado: ${ticketId}`); return result.rows[0]; } return null; } catch (error) { logError(error, `❌ Erro ao atualizar ticket ${ticketId}`); throw error; } } } /** * @module TicketSnModel * @description Este módulo fornece métodos para interagir com a tabela de tickets do ServiceNow (tickets_sn) no banco de dados. * Inclui funcionalidades para salvar um ticket, buscar tickets pendentes para sincronização com o GLPI, * e buscar/atualizar tickets específicos. * * A função saveTicket insere ou atualiza um ticket na tabela tickets_sn. * A função getPendingTickets busca tickets que ainda não foram sincronizados com o GLPI (não possuem registro em ticket_sync) * e ignora os tickets com status 'Encerrado' ou 'Encerrado - Omitido'. */ module.exports = TicketSnModel;