const geogridService = require("../../shared/apis/geogridService.js"); const googleService = require("../../shared/apis/googleService.js"); const cepRestService = require("../../shared/apis/cepRestService.js"); const hubsoftService = require("../../shared/apis/hubsoftService.js"); const logger = require('../../shared/utils/logger.js'); const repository = require('./contratacao.repository.js'); // const viaCepService = require("../../shared/apis/viaCepService.js"); const {ViabilidadeModel, ClientModelPf, ClientModelPj, ProspectModel} = require('./contratacao.model'); // utilitário para ler chaves do payload tolerante a ":" e espaços const getPayloadValue = (obj, key) => { if (!obj) return undefined; // variações diretas const variants = [key, `${key}:`, `${key}: `]; for (const v of variants) if (Object.prototype.hasOwnProperty.call(obj, v)) return obj[v]; // tentativa de correspondência normalizada (ignora ":" e case) const nk = String(key).trim().toLowerCase(); for (const k of Object.keys(obj)) { const kn = String(k).trim().replace(/:$/, '').toLowerCase(); if (kn === nk) return obj[k]; } return undefined; }; // Classe de erro personalizada para o serviço class ServiceError extends Error { constructor(message, statusCode = 500) { super(message); this.name = 'ServiceError'; this.statusCode = statusCode; } } // Função para verificar a viabilidade de um endereço async function verificarViabilidade(rawViabilidadeData) { const rawCep = rawViabilidadeData.cep; const rawNumero = rawViabilidadeData.numero; if (!rawCep || !rawNumero) { logger.warn('CEP ou número não fornecidos na verificação de viabilidade.'); throw new ServiceError('CEP e número são obrigatórios.', 400); } // // Consulta o endereço completo usando a API externa de CEP // const addressData = await viaCepService.getConsultaCep(rawCep); // if (!addressData) { // logger.warn('Endereço não encontrado ou resposta inválida do serviço de CEP', { cep: rawCep, response: addressData }); // throw new ServiceError('Endereço não encontrado para o CEP fornecido.', 404); // } // logger.info('Endereço obtido com sucesso via CEP', { data: addressData }); // const { logradouro, bairro, localidade: city, uf: state, cep } = addressData; // FIX: Usar address.data para desestruturar // const addressString = `${logradouro}, ${rawNumero}, ${bairro}, ${city}, ${state}, ${cep}`; // logger.info('String de endereço montada para geocodificação', { addressString }); const address = await cepRestService.getConsultaCep(rawCep); // FIX: Revertendo para a verificação correta da resposta do cepRestService if (!address) { logger.warn('Endereço não encontrado ou resposta inválida do serviço de CEP', { cep: rawCep, response: address }); throw new ServiceError('Endereço não encontrado para o CEP fornecido.', 404); } logger.info('Endereço obtido com sucesso via CEP', { data: address }); const { logradouro, bairro, localidade: city, uf: state, cep } = address; // FIX: Usar address.data para desestruturar const addressString = `${logradouro}, ${rawNumero}, ${bairro}, ${city}, ${state}, ${cep}`; logger.info('String de endereço montada para geocodificação', { addressString }); const coords = await googleService.geocodeWithGoogle(addressString); if (!coords) { logger.error('Não foi possível obter coordenadas do Google Geocoding', { addressString }); throw new ServiceError('Não foi possível obter as coordenadas do endereço.', 500); } logger.info('Coordenadas obtidas com sucesso', { coords }); const viabilidade = await geogridService.consultaViabilidade(coords.lat, coords.lon); let naoDedicado = false; let dedicado = false; if (viabilidade.data) { const distancia = viabilidade.data.distancia; logger.info(`Distância para o ponto de presença: ${distancia}m`); if (distancia <= 500) { naoDedicado = true; dedicado = true; } else if (distancia <= 1000) { naoDedicado = false; dedicado = true; } } else { logger.warn('Resposta do GeoGrid não continha dados de viabilidade.', { cep: rawCep, numero: rawNumero }); } const viabilidadeResult = new ViabilidadeModel(rawViabilidadeData.nome, rawViabilidadeData.email, rawViabilidadeData.telefone, logradouro, rawNumero, bairro, city, state, cep, naoDedicado, dedicado); await repository.insertViabilidadeData(viabilidadeResult) .then(() => { logger.info('Dados de viabilidade inseridos no repositório com sucesso', { viabilidadeResult }); }) .catch((err) => { logger.error('Erro ao inserir dados de viabilidade no repositório', { message: err.message, stack: err.stack }); }); return viabilidadeResult; } async function criarProspecto(rawProspectData) { try { const payload = (rawProspectData && rawProspectData.prospectData) ? rawProspectData.prospectData : rawProspectData; const planos = { '100 Mega + Super WiFi': { id_servico: '22'}, '200 Mega + Super WiFi': { id_servico: '94'}, '500 Mega + Super WiFi': { id_servico: '96'}, '700 Mega + Super WiFi': { id_servico: '97'}, }; const planoKey = getPayloadValue(payload, 'Descrição'); const servico = planos[planoKey]; const celular = Number(String(getPayloadValue(payload, 'Celular')).replace(/\D/g, '')); if (!servico) { logger.warn('Plano não encontrado no mapeamento', { planoKey, disponíveis: Object.keys(planos) }); throw new ServiceError('Plano inválido ou não encontrado.', 400); } // parse do valor mensal de forma robusta (aceita "R$ 119,90", "119,90", etc) const rawValor = getPayloadValue(payload, 'Valor (mensal)') || getPayloadValue(payload, 'Valor (mensal):') || getPayloadValue(payload, 'Valor'); if (rawValor) { const sanitized = String(rawValor).replace(/[^\d,.-]/g, '').replace(/\.(?=\d{3})/g, '').replace(',', '.'); const valorNum = Number(sanitized); if (!Number.isNaN(valorNum)) servico.valor = valorNum; } servico.id_servico = Number(servico.id_servico); if (!servico) { logger.warn('Plano não encontrado no mapeamento', { planoKey, disponíveis: Object.keys(planos) }); throw new ServiceError('Plano inválido ou não encontrado.', 400); } if (payload["CPF:"] === "") { // Pessoa Jurídica const clientData = new ClientModelPj( payload["Razão Social:"], payload["E-mail:"], payload["CEP:"], payload["Número:"], payload["Logradouro:"], payload["Bairro:"], payload["Cidade:"], payload["Estado:"], servico, payload["Telefone:"], "pj", payload["Endereço de cobrança"], payload["CNPJ:"], payload["Selecione o dia de vencimento:"], payload["Escolha a forma de pagamento:"], payload["Banco:"], `${payload["Agência:"]}-${payload["Dígito (agência):"]}`, payload["Conta:"], payload["E-mail de cobrança:"], ); logger.info('Criando prospecto PJ no Hubsoft', { clientData }); const resultado = await hubsoftService.criarProspectoHubsoft(clientData); logger.info('Prospecto PJ criado com sucesso no Hubsoft', { resultado }); return resultado; } else { // Pessoa Física const clientData = new ClientModelPf( payload["Nome completo:"], payload["E-mail:"], payload["CEP:"], payload["Número:"], payload["Logradouro:"], payload["Bairro:"], payload["Cidade:"], payload["Estado:"], servico, payload["Plano:"], payload["CPF:"], celular, "pf", payload["Endereço de cobrança"], payload["Nome da mãe:"], payload["Data de nascimento:"], payload["Selecione o dia de vencimento:"], payload["Escolha a forma de pagamento:"], payload["Banco:"], `${payload["Agência:"]}-${payload["Dígito (agência):"]}`, payload["Conta:"], payload["E-mail de cobrança:"], ); const prospectData = new ProspectModel( getPayloadValue(payload, 'CEP'), servico, // passa o objeto { id_servico, valor } getPayloadValue(payload, 'Nome completo'), getPayloadValue(payload, 'CPF'), celular, getPayloadValue(payload, 'E-mail'), "pf", getPayloadValue(payload, 'Bairro'), getPayloadValue(payload, 'Logradouro'), getPayloadValue(payload, 'Número'), getPayloadValue(payload, 'Nome da mãe') || "" ); // serializa para JSON plano conforme toJSON() const prospectPayload = (typeof prospectData.toJSON === 'function') ? prospectData.toJSON() : JSON.parse(JSON.stringify(prospectData)); // envia objeto plano ao hubsoft (o hubsoftService já monta { prospectData }) await hubsoftService.criarProspectHubsoft(prospectPayload); logger.info('Criando prospecto PF', { prospectPayload }); return prospectPayload; } } catch (error) { logger.error('Erro ao criar prospecto', { message: error.message, stack: error.stack }); throw new ServiceError('Erro ao criar prospecto.', 500); } } module.exports = { verificarViabilidade, criarProspecto }; /* DESCRIÇÃO: Este arquivo é a camada de "Serviço" para o módulo de contratação. Ele contém a lógica de negócio principal da aplicação, orquestrando chamadas a diferentes APIs externas e processando os dados para atender às solicitações do `contratacao.controller.js`. A classe `ServiceError` é uma classe de erro personalizada para permitir que os serviços lancem exceções com `status codes` HTTP específicos, que podem ser capturados e tratados pelo controller. FUNÇÕES: - verificarViabilidade(rawCep, rawNumero): 1. É chamado pelo `contratacao.controller.js` com o CEP e o número do endereço. 2. Valida se o CEP e o número foram fornecidos, lançando um `ServiceError` (400) se não forem. 3. Chama o `cepRestService` para obter os dados do endereço a partir do CEP. Se não encontrar, lança um `ServiceError` (404). 4. Monta uma string de endereço completo e a utiliza para obter as coordenadas geográficas (latitude e longitude) através do `googleService`. Lança um erro (500) se não conseguir obter as coordenadas. 5. Com as coordenadas, chama o `geogridService` para consultar a viabilidade do serviço, que retorna a distância de um ponto de presença. 6. Com base na distância retornada pelo GeoGrid, define as flags `naoDedicado` e `dedicado` (ex: se a distância for menor que 500m, ambos são `true`). 7. Retorna um objeto contendo os dados do endereço e as flags de viabilidade para o controller. - criarProspecto(prospectData): 1. Recebe os dados do prospecto do `contratacao.controller.js`. 2. Chama o `hubsoftService` para criar o prospecto no sistema Hubsoft. 3. Retorna o resultado da criação. 4. Em caso de erro na comunicação com o Hubsoft, lança um `ServiceError` (500). Este serviço é o cérebro do módulo, conectando múltiplas fontes de dados externas para fornecer uma resposta unificada sobre a viabilidade e para registrar novos clientes. */