FEAT: Adição de parametros que ainda não estavam indo para o prospecto, e fallback de consultas de CEP.

- Adicionado valor de ID de CRM fixo e ID de origem-cliente fixo para envio do prospecto.

- Criado fallback das APIs de consulta de CEP, se caso o ViaCEP falhar, o CepRest assume, mantendo o fluxo fluido.

- 5s de timeout adicionados nas requisições para o ViaCep e CepRest.

- Logs mais estruturados adicionados.
This commit is contained in:
Gabriel Amancio 2026-01-08 14:01:03 -03:00
parent 26fc47f989
commit 2679e86e2e
5 changed files with 116 additions and 78 deletions

View File

@ -3,7 +3,7 @@ const logger = require('../../shared/utils/logger.js');
async function handleViabilidade(req, res) {
const rawViabilidadeData = req.body;
logger.info('Recebida requisição de viabilidade', { rawViabilidadeData });
try {
const resultadoViabilidade = await contratacaoService.verificarViabilidade(rawViabilidadeData);
return res.json(resultadoViabilidade);
@ -20,6 +20,10 @@ async function handleCriarProspecto(req, res) {
try {
const resultado = await contratacaoService.criarProspecto(prospectData);
if (resultado.status === 'error') {
logger.error('Erro ao criar prospecto', { resultado });
return res.status(500).json({ error: resultado.msg || "Erro ao criar o prospecto." });
}
logger.info('Prospecto criado com sucesso', { resultado });
return res.json({ message: 'Prospecto criado com sucesso', data: resultado });

View File

@ -73,20 +73,29 @@ class ClientModelPj {
}
class ProspectModel {
constructor(cep, servico, nomeRazaoSocial, cpfCnpj, telefone, email, tipoPessoa, bairro, endereco, numero, nomeMae, idCrm, complemento, dataNascimento) {
// ordem: cep, servico, nomeRazaoSocial, cpfCnpj, telefone, email, tipoPessoa,
// bairro, endereco, numero, nomeMae, idCrm, complemento, dataNascimento
constructor(cep, servico, nomeRazaoSocial, cpfCnpj, telefone, email, tipoPessoa, bairro, endereco, numero, nomeMae, complemento, dataNascimento) {
this.cep = cep;
this.servico = servico;
this.cpf_cnpj = cpfCnpj;
this.telefone = telefone;
this.nome_razaosocial = nomeRazaoSocial;
// manter ambos formatos (snake e camel) para compatibilidade
this.tipo_pessoa = tipoPessoa;
this.tipoPessoa = tipoPessoa;
this.bairro = bairro;
this.endereco = endereco;
this.numero = numero;
this.email = email;
this.nome_mae = nomeMae;
this.nomeMae = nomeMae;
this.id_crm = idCrm;
this.complemento = complemento;
this.data_nascimento = dataNascimento;
this.dataNascimento = dataNascimento;
}
@ -97,18 +106,19 @@ class ProspectModel {
id_servico: this.servico.id_servico ?? this.servico.id ?? null,
valor: this.servico.valor ?? null
} : null,
nome_razaosocial: this.nome_razaosocial,
cpf_cnpj: this.cpf_cnpj,
telefone: this.telefone,
nome_razaosocial: this.nome_razaosocial,
tipo_pessoa: this.tipoPessoa,
bairro: this.bairro,
numero: this.numero,
endereco: this.endereco,
email: this.email,
nome_mae: this.nomeMae,
id_crm: this.id_crm,
tipo_pessoa: this.tipo_pessoa,
bairro: this.bairro,
endereco: this.endereco,
numero: this.numero,
nome_mae: this.nome_mae,
id_crm: 4,
data_nascimento: this.data_nascimento,
complemento: this.complemento,
data_nascimento: this.dataNascimento
id_origem_cliente: 51
};
}
pjToJSON() {
@ -122,11 +132,13 @@ class ProspectModel {
cpf_cnpj: this.cpf_cnpj,
telefone: this.telefone,
email: this.email,
tipo_pessoa: this.tipoPessoa,
tipo_pessoa: this.tipo_pessoa,
id_crm: 4,
bairro: this.bairro,
endereco: this.endereco,
numero: this.numero,
complemento: this.complemento
complemento: this.complemento,
id_origem_cliente: 51
};
}
}

View File

@ -1,11 +1,11 @@
const geogridService = require("../../shared/apis/geogridService.js");
const googleService = require("../../shared/apis/googleService.js");
// const cepRestService = require("../../shared/apis/cepRestService.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');
const { ViabilidadeModel, ClientModelPf, ClientModelPj, ProspectModel } = require('./contratacao.model');
// utilitário para ler chaves do payload tolerante a ":" e espaços
const getPayloadValue = (obj, key) => {
@ -37,37 +37,46 @@ class ServiceError extends Error {
async function verificarViabilidade(rawViabilidadeData) {
const rawCep = rawViabilidadeData.cep;
const rawNumero = rawViabilidadeData.numero;
const source = rawViabilidadeData.source || '';
let address;
let addressString;
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);
// Obtém o endereço completo usando a API de CEP (viaCEP e cepRestService como fallback)
try {
address = await viaCepService.getConsultaCep(rawCep);
if (!address || !address.logradouro) {
throw new Error('Resposta inválida do viaCEP');
}
logger.info('Endereço obtido com sucesso via viaCEP', { address });
} catch (err) {
address = await cepRestService.getConsultaCep(rawCep);
if (!address || !address.logradouro) {
throw new Error('Resposta inválida do cepRestService');
}
logger.info('Endereço obtido com sucesso via cepRestService', { address });
}
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 });
if (!address) {
throw new ServiceError('Não foi possível obter endereço para o CEP informado.', 502);
}
// 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 });
// Obtém as coordenadas geográficas do endereço usando o Google Geocoding API
const { logradouro, bairro, localidade: city, uf: state, cep } = address;
addressString = `${logradouro}, ${rawNumero}, ${bairro}, ${city}, ${state}, ${cep}`;
logger.info('Endereço montado para geocodificação', { addressString });
// 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 });
@ -97,15 +106,20 @@ async function verificarViabilidade(rawViabilidadeData) {
logger.warn('Dados de viabilidade não contêm informação de distância', { viabilidadeData: viabilidade.data });
}
const viabilidadeResult = new ViabilidadeModel(rawViabilidadeData.nome, rawViabilidadeData.email, rawViabilidadeData.telefone, logradouro, rawNumero, bairro, city, state, cep, naoDedicado, dedicado, distancia || null);
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 });
});
if (source) {
return viabilidadeResult;
}
try {
await repository.insertViabilidadeData(viabilidadeResult);
logger.info('Viabilidade salva com sucesso');
} catch (err) {
logger.error('Erro ao salvar viabilidade', err);
}
return viabilidadeResult;
}
@ -115,11 +129,11 @@ async function criarProspecto(rawProspectData) {
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'},
'1 Gb + Super WiFi': { id_servico: '32'},
'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' },
'1 Gb + Super WiFi': { id_servico: '32' },
};
const planoKey = getPayloadValue(payload, 'Descrição');
@ -144,47 +158,55 @@ async function criarProspecto(rawProspectData) {
throw new ServiceError('Plano inválido ou não encontrado.', 400);
}
//função que troca a / da data de nascimento por -
const formatDate = (dateStr) => {
if (!dateStr) return "";
return dateStr.replace(/\//g, '-');
};
const dataNascimento = formatDate(getPayloadValue(payload, 'Data de nascimento') || "");
if (payload["CPF:"] === "") {
// Pessoa Jurídica
const prospectData = new ProspectModel(
getPayloadValue(payload, 'CEP'),
servico, // passa o objeto { id_servico, valor }
getPayloadValue(payload, 'Razão Social'),
getPayloadValue(payload, 'CNPJ'),
celular,
getPayloadValue(payload, 'E-mail'),
"pj",
getPayloadValue(payload, 'Bairro'),
getPayloadValue(payload, 'Logradouro'),
getPayloadValue(payload, 'Número'),
4, // ID fixo do CRM Vendas
getPayloadValue(payload, 'Complemento') || ""
servico, // objeto { id_servico, valor }
getPayloadValue(payload, 'Razão Social'), // nomeRazaoSocial
getPayloadValue(payload, 'CNPJ'), // cpfCnpj
celular, // telefone
getPayloadValue(payload, 'E-mail'), // email
"pj", // tipoPessoa
getPayloadValue(payload, 'Bairro'), // bairro
getPayloadValue(payload, 'Logradouro'), // endereco
getPayloadValue(payload, 'Número'), // numero
"", // nomeMae (não aplicável para PJ)
getPayloadValue(payload, 'Complemento') || "", // complemento
"" // dataNascimento (não aplicável)
);
console.log(prospectData);
// serializa para JSON plano conforme toJSON()
const prospectPayload = (typeof prospectData.pjToJSON === 'function')
? prospectData.pjToJSON()
: JSON.parse(JSON.stringify(prospectData));
await hubsoftService.criarProspectHubsoft(prospectPayload);
return prospectPayload;
} else {
// clientData não sendo usado, pois o e-mail está sendo enviado pelo frontend.
// Pessoa Física
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') || "",
4, // ID fixo do CRM Vendas
getPayloadValue(payload, 'Complemento') || "",
getPayloadValue(payload, 'Data de nascimento') || ""
servico, // objeto { id_servico, valor }
getPayloadValue(payload, 'Nome completo'), // nomeRazaoSocial
getPayloadValue(payload, 'CPF'), // cpfCnpj
celular, // telefone
getPayloadValue(payload, 'E-mail'), // email
"pf", // tipoPessoa
getPayloadValue(payload, 'Bairro'), // bairro
getPayloadValue(payload, 'Logradouro'), // endereco
getPayloadValue(payload, 'Número'), // numero
getPayloadValue(payload, 'Nome da mãe') || "", // nomeMae
getPayloadValue(payload, 'Complemento') || "", // complemento
dataNascimento // dataNascimento
);
console.log(prospectData);
// serializa para JSON plano conforme toJSON()
@ -193,10 +215,10 @@ async function criarProspecto(rawProspectData) {
: JSON.parse(JSON.stringify(prospectData));
// envia objeto plano ao hubsoft (o hubsoftService já monta { prospectData })
await hubsoftService.criarProspectHubsoft(prospectPayload);
const result = await hubsoftService.criarProspectHubsoft(prospectPayload);
logger.info('Criando prospecto PF', { prospectPayload });
return prospectPayload;
return result;
}
} catch (error) {
logger.error('Erro ao criar prospecto', { message: error.message, stack: error.stack });

View File

@ -19,7 +19,7 @@ const getConsultaCep = async (rawCep) => {
try {
const cepRestUrl = 'https://api.cep.rest/';
const address = await axios.post(cepRestUrl, { cep });
const address = await axios.post(cepRestUrl, { cep }, { timeout: 5000 });
console.log(address.data);
// Tratamento de resposta conforme diferentes estruturas possíveis

View File

@ -14,7 +14,7 @@ const getConsultaCep = async (rawCep) => {
}
try {
const viaCepUrl = `https://viacep.com.br/ws/${cep}/json/`;
const response = await axios.get(viaCepUrl);
const response = await axios.get(viaCepUrl, { timeout: 5000 });
if (response.data.erro) {
logger.warn('CEP não encontrado na API ViaCEP', { cep });
return null; // Controller tratará como 'Endereço não encontrado'