fix TopSapp
This commit is contained in:
1 parent
8dc6c594c9
commit
bae6adefcb
42 files changed
+43645
-105
No files matched your search
+308
-11
@@ -195,31 +195,163 @@ class TopSappAPI {
|
||||
// Obter sessão válida para pegar o idUsuario
|
||||
const session = await this.getValidSession(instance, credentials);
|
||||
|
||||
// Tentar buscar por diferentes critérios
|
||||
console.log(`🔍 Buscando cliente: "${query}" na instância: ${instance.name}`);
|
||||
|
||||
const searchParams = {
|
||||
idUsuario: session.idUsuario || 1 // Usar ID do usuário da sessão
|
||||
idUsuario: session.idUsuario || 1
|
||||
};
|
||||
|
||||
// Se é número, pode ser CPF ou ID
|
||||
if (/^\d+$/.test(query)) {
|
||||
if (query.length === 11 || query.length === 14) {
|
||||
// Determinar tipo de busca
|
||||
const queryLimpo = query.trim();
|
||||
|
||||
if (/^\d+$/.test(queryLimpo)) {
|
||||
// Apenas números - pode ser CPF ou ID
|
||||
if (queryLimpo.length === 11 || queryLimpo.length === 14) {
|
||||
// CPF (11 dígitos) ou CNPJ (14 dígitos)
|
||||
searchParams.cpfCnpj = query;
|
||||
console.log(`📋 Buscando por CPF/CNPJ: ${queryLimpo}`);
|
||||
searchParams.cpfCnpj = queryLimpo;
|
||||
} else {
|
||||
// ID do cliente
|
||||
searchParams.idCliente = query;
|
||||
console.log(`🆔 Buscando por ID: ${queryLimpo}`);
|
||||
searchParams.idCliente = parseInt(queryLimpo, 10);
|
||||
}
|
||||
} else {
|
||||
// CPF formatado ou nome - usar o endpoint de busca geral
|
||||
searchParams.cpfCnpj = query.replace(/[^\d]/g, ''); // Remove formatação
|
||||
// Contém letras - é nome ou CPF formatado
|
||||
const apenasNumeros = queryLimpo.replace(/[^\d]/g, '');
|
||||
|
||||
if (apenasNumeros.length === 11 || apenasNumeros.length === 14) {
|
||||
// CPF/CNPJ formatado
|
||||
console.log(`📋 Buscando por CPF/CNPJ formatado: ${queryLimpo} -> ${apenasNumeros}`);
|
||||
searchParams.cpfCnpj = apenasNumeros;
|
||||
} else {
|
||||
// Nome do cliente
|
||||
console.log(`👤 Buscando por nome: ${queryLimpo}`);
|
||||
searchParams.nome = queryLimpo;
|
||||
|
||||
// Fazer busca sem filtros específicos para pegar todos os clientes
|
||||
// e depois filtrar localmente por nome
|
||||
delete searchParams.nome; // Remove o parâmetro nome para buscar todos
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.makeRequest('ObterClientes', searchParams, instance, credentials);
|
||||
let result;
|
||||
|
||||
// Se é busca por nome, fazer busca geral e filtrar localmente
|
||||
if (!searchParams.cpfCnpj && !searchParams.idCliente) {
|
||||
console.log(`🔄 Fazendo busca geral para filtrar por nome: "${queryLimpo}"`);
|
||||
|
||||
// Buscar todos os clientes
|
||||
result = await this.makeRequest('ObterClientes', searchParams, instance, credentials);
|
||||
|
||||
if (result.success && result.data.dados) {
|
||||
const todosClientes = result.data.dados;
|
||||
console.log(`📊 Total de clientes retornados: ${todosClientes.length}`);
|
||||
|
||||
// Filtrar por nome localmente com diferentes estratégias
|
||||
const nomeQuery = queryLimpo.toLowerCase()
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, ''); // Remove acentos
|
||||
|
||||
const clientesFiltrados = todosClientes.filter(cliente => {
|
||||
const nomeCliente = (cliente.nome || '').toLowerCase()
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, ''); // Remove acentos
|
||||
|
||||
// Estratégias de busca:
|
||||
// 1. Nome completo exato
|
||||
if (nomeCliente === nomeQuery) return true;
|
||||
|
||||
// 2. Nome contém a busca
|
||||
if (nomeCliente.includes(nomeQuery)) return true;
|
||||
|
||||
// 3. Busca contém o nome (para nomes parciais)
|
||||
if (nomeQuery.includes(nomeCliente) && nomeCliente.length > 3) return true;
|
||||
|
||||
// 4. Busca por palavras individuais
|
||||
const palavrasBusca = nomeQuery.split(/\s+/).filter(p => p.length > 2);
|
||||
const palavrasNome = nomeCliente.split(/\s+/);
|
||||
|
||||
// Verificar se todas as palavras da busca estão no nome
|
||||
const todasPalavrasEncontradas = palavrasBusca.every(palavraBusca =>
|
||||
palavrasNome.some(palavraNome =>
|
||||
palavraNome.includes(palavraBusca) || palavraBusca.includes(palavraNome)
|
||||
)
|
||||
);
|
||||
|
||||
if (todasPalavrasEncontradas && palavrasBusca.length > 0) return true;
|
||||
|
||||
// 5. Busca fuzzy - verificar similaridade
|
||||
if (this.calcularSimilaridade(nomeCliente, nomeQuery) > 0.8) return true;
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
console.log(`🎯 Clientes filtrados por nome: ${clientesFiltrados.length}`);
|
||||
|
||||
// Ordenar por relevância (nomes mais similares primeiro)
|
||||
clientesFiltrados.sort((a, b) => {
|
||||
const nomeA = (a.nome || '').toLowerCase();
|
||||
const nomeB = (b.nome || '').toLowerCase();
|
||||
const simA = this.calcularSimilaridade(nomeA, nomeQuery);
|
||||
const simB = this.calcularSimilaridade(nomeB, nomeQuery);
|
||||
return simB - simA; // Ordem decrescente de similaridade
|
||||
});
|
||||
|
||||
// Log dos resultados para debug
|
||||
if (clientesFiltrados.length > 0) {
|
||||
console.log(`✅ Clientes encontrados:`);
|
||||
clientesFiltrados.slice(0, 5).forEach((cliente, index) => {
|
||||
console.log(` ${index + 1}. ${cliente.nome} (ID: ${cliente.id})`);
|
||||
});
|
||||
} else {
|
||||
console.log(`❌ Nenhum cliente encontrado para: "${queryLimpo}"`);
|
||||
|
||||
// Log de amostra para debug
|
||||
console.log(`🔍 Amostra de nomes disponíveis:`);
|
||||
todosClientes.slice(0, 5).forEach((cliente, index) => {
|
||||
console.log(` ${index + 1}. ${cliente.nome}`);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
clientes: clientesFiltrados.map(cliente => ({
|
||||
...cliente,
|
||||
cpf: cliente.cpfCnpj || cliente.cpf || cliente.documento || '',
|
||||
status: this.interpretarStatusCliente(cliente),
|
||||
statusOriginal: cliente.status,
|
||||
id_status: cliente.id_status,
|
||||
bloqueado: cliente.bloqueado,
|
||||
ativo: cliente.ativo
|
||||
})),
|
||||
instance: result.instance,
|
||||
debug: {
|
||||
totalOriginal: todosClientes.length,
|
||||
totalFiltrado: clientesFiltrados.length,
|
||||
termoBusca: queryLimpo
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Busca específica (CPF/ID)
|
||||
result = await this.makeRequest('ObterClientes', searchParams, instance, credentials);
|
||||
}
|
||||
|
||||
if (result.success) {
|
||||
// Normalizar dados dos clientes para incluir CPF e status correto
|
||||
const clientesNormalizados = (result.data.dados || []).map(cliente => ({
|
||||
...cliente,
|
||||
cpf: cliente.cpfCnpj || cliente.cpf || cliente.documento || '',
|
||||
status: this.interpretarStatusCliente(cliente),
|
||||
statusOriginal: cliente.status,
|
||||
id_status: cliente.id_status,
|
||||
bloqueado: cliente.bloqueado,
|
||||
ativo: cliente.ativo
|
||||
}));
|
||||
|
||||
return {
|
||||
success: true,
|
||||
clientes: result.data.dados || [], // Usar 'dados' em vez de 'clientes'
|
||||
clientes: clientesNormalizados,
|
||||
instance: result.instance
|
||||
};
|
||||
}
|
||||
@@ -227,6 +359,7 @@ class TopSappAPI {
|
||||
return result;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Erro na busca de cliente:`, error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
@@ -235,6 +368,93 @@ class TopSappAPI {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter dados detalhados do cliente incluindo dados cadastrais completos
|
||||
* Usa apenas os dados dos serviços que já contêm informações completas do cliente
|
||||
*/
|
||||
async obterDadosCompletosCliente(idCliente, location = '', credentials) {
|
||||
const instance = this.getInstanceByLocation(location);
|
||||
|
||||
try {
|
||||
console.log(`🔍 Obtendo dados completos do cliente ${idCliente} via serviços (evitando erro 400)`);
|
||||
|
||||
// Usar apenas dados dos serviços que já contêm informações completas
|
||||
const servicosData = await this.obterServicosCliente(idCliente, location, credentials);
|
||||
|
||||
if (!servicosData.success) {
|
||||
console.log(`❌ Falha ao obter serviços: ${servicosData.error}`);
|
||||
return servicosData;
|
||||
}
|
||||
|
||||
const servicos = servicosData.data.dados || servicosData.data.servicos || [];
|
||||
|
||||
if (servicos.length === 0) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Cliente não encontrado ou sem serviços'
|
||||
};
|
||||
}
|
||||
|
||||
// Usar dados do primeiro serviço para extrair informações do cliente
|
||||
const primeiroServico = servicos[0];
|
||||
console.log(`✅ Extraindo dados do cliente dos serviços: ${primeiroServico.instalacao_nome}`);
|
||||
|
||||
// Montar dados do cliente baseado nas informações dos serviços
|
||||
const cliente = {
|
||||
id: primeiroServico.id_cliente,
|
||||
nome: primeiroServico.instalacao_nome || 'Nome não informado',
|
||||
cpf: primeiroServico.cpf || '',
|
||||
rg: primeiroServico.rg || '',
|
||||
email: primeiroServico.usuario || '',
|
||||
telefone: primeiroServico.instalacao_contato1 || primeiroServico.instalacao_contato || '',
|
||||
celular: primeiroServico.instalacao_contato1 || '',
|
||||
endereco: primeiroServico.instalacao_endereco || '',
|
||||
numero: primeiroServico.instalacao_numero || '',
|
||||
bairro: primeiroServico.instalacao_bairro || '',
|
||||
cidade: primeiroServico.instalacao_cidade || '',
|
||||
uf: primeiroServico.instalacao_uf || '',
|
||||
cep: primeiroServico.instalacao_cep || '',
|
||||
complemento: primeiroServico.instalacao_complemento || '',
|
||||
data_cadastro: primeiroServico.data_cadastro || '',
|
||||
observacoes: primeiroServico.observacoes || '',
|
||||
// Determinar status baseado nos serviços
|
||||
status: this.determinarStatusClientePorServicos(servicos),
|
||||
// Dados de localização
|
||||
latitude: primeiroServico.latitude || '',
|
||||
longitude: primeiroServico.longitude || ''
|
||||
};
|
||||
|
||||
console.log(`✅ Dados do cliente extraídos com sucesso`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
cliente: cliente,
|
||||
instance: instance.name,
|
||||
fonte: 'servicos'
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Erro ao obter dados completos: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
instance: instance.name
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determinar status do cliente baseado nos serviços
|
||||
*/
|
||||
determinarStatusClientePorServicos(servicos) {
|
||||
// Se tem pelo menos um serviço ativo, considera cliente ativo
|
||||
const temServicoAtivo = servicos.some(servico =>
|
||||
servico.id_status === 1 || servico.status === 'liberado'
|
||||
);
|
||||
|
||||
return temServicoAtivo ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter dados detalhados do cliente
|
||||
*/
|
||||
@@ -452,6 +672,83 @@ class TopSappAPI {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpretar status do cliente corretamente
|
||||
*/
|
||||
interpretarStatusCliente(cliente) {
|
||||
// Verificar diferentes campos de status que podem indicar se o cliente está ativo
|
||||
|
||||
// Se tem campo 'ativo' explícito
|
||||
if (typeof cliente.ativo !== 'undefined') {
|
||||
return cliente.ativo === 1 || cliente.ativo === true ? 1 : 0;
|
||||
}
|
||||
|
||||
// Se tem campo 'bloqueado'
|
||||
if (typeof cliente.bloqueado !== 'undefined') {
|
||||
return cliente.bloqueado === 0 || cliente.bloqueado === false ? 1 : 0;
|
||||
}
|
||||
|
||||
// Verificar por id_status (alguns sistemas usam diferentes códigos)
|
||||
if (cliente.id_status) {
|
||||
// Status 1 = Ativo, 2 = Bloqueado, 3 = Desativado, etc.
|
||||
return cliente.id_status === 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Se tem data de desativação, está inativo
|
||||
if (cliente.data_desativacao && cliente.data_desativacao !== '0000-00-00' && cliente.data_desativacao !== null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Se tem data de bloqueio sem data de desbloqueio, está bloqueado
|
||||
if (cliente.data_bloqueio && cliente.data_bloqueio !== '0000-00-00' && cliente.data_bloqueio !== null) {
|
||||
if (!cliente.data_desbloqueio || cliente.data_desbloqueio === '0000-00-00' || cliente.data_desbloqueio === null) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Por padrão, usar o status original ou assumir ativo se não há informação de bloqueio/desativação
|
||||
return cliente.status || 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcular similaridade entre duas strings (algoritmo de Jaro-Winkler simplificado)
|
||||
*/
|
||||
calcularSimilaridade(str1, str2) {
|
||||
if (str1 === str2) return 1.0;
|
||||
if (str1.length === 0 || str2.length === 0) return 0.0;
|
||||
|
||||
// Normalizar strings
|
||||
const s1 = str1.toLowerCase().trim();
|
||||
const s2 = str2.toLowerCase().trim();
|
||||
|
||||
if (s1 === s2) return 1.0;
|
||||
|
||||
// Calcular distância de Levenshtein simplificada
|
||||
const maxLen = Math.max(s1.length, s2.length);
|
||||
let matches = 0;
|
||||
|
||||
for (let i = 0; i < Math.min(s1.length, s2.length); i++) {
|
||||
if (s1[i] === s2[i]) matches++;
|
||||
}
|
||||
|
||||
// Adicionar pontos por palavras em comum
|
||||
const words1 = s1.split(/\s+/);
|
||||
const words2 = s2.split(/\s+/);
|
||||
|
||||
let wordMatches = 0;
|
||||
words1.forEach(word1 => {
|
||||
if (words2.some(word2 => word1.includes(word2) || word2.includes(word1))) {
|
||||
wordMatches++;
|
||||
}
|
||||
});
|
||||
|
||||
const charSim = matches / maxLen;
|
||||
const wordSim = wordMatches / Math.max(words1.length, words2.length);
|
||||
|
||||
// Média ponderada: 60% caracteres, 40% palavras
|
||||
return (charSim * 0.6) + (wordSim * 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TopSappAPI;
|
||||
+306
-17
@@ -49,24 +49,10 @@ class TopSappManager {
|
||||
const result = await this.api.buscarCliente(query, location, credentials);
|
||||
|
||||
if (result.success && result.clientes) {
|
||||
// Formatar resultados para melhor apresentação
|
||||
const clientesFormatados = result.clientes.map(cliente => ({
|
||||
id: cliente.id,
|
||||
nome: cliente.nome,
|
||||
cpf: cliente.cpf,
|
||||
email: cliente.email,
|
||||
telefone: cliente.telefone,
|
||||
endereco: `${cliente.endereco}, ${cliente.numero} - ${cliente.bairro}`,
|
||||
cidade: cliente.cidade,
|
||||
status: cliente.status,
|
||||
instancia: result.instance
|
||||
}));
|
||||
|
||||
return {
|
||||
success: true,
|
||||
clientes: clientesFormatados,
|
||||
total: clientesFormatados.length,
|
||||
instancia: result.instance
|
||||
...result,
|
||||
total: result.clientes.length,
|
||||
instancia: result.instance || location
|
||||
};
|
||||
}
|
||||
|
||||
@@ -80,6 +66,134 @@ class TopSappManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter dados cadastrais completos do cliente
|
||||
*/
|
||||
async obterDadosCadastrais(idCliente, location = '') {
|
||||
if (!this.isReady()) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'TopSapp não está configurado'
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const credentials = this.config.getCredentialsByLocation(location);
|
||||
|
||||
console.log(`🔧 Obtendo dados cadastrais do cliente ${idCliente} (usando apenas serviços para evitar erro 400)`);
|
||||
|
||||
// Buscar dados completos do cliente e serviços em paralelo
|
||||
const [dadosCliente, servicos] = await Promise.all([
|
||||
this.api.obterDadosCompletosCliente(idCliente, location, credentials),
|
||||
this.api.obterServicosCliente(idCliente, location, credentials)
|
||||
]);
|
||||
|
||||
if (!dadosCliente.success) {
|
||||
console.log(`❌ Falha ao obter dados do cliente: ${dadosCliente.error}`);
|
||||
return dadosCliente;
|
||||
}
|
||||
|
||||
console.log(`✅ Dados do cliente obtidos com sucesso: ${dadosCliente.cliente.nome}`);
|
||||
|
||||
const resultado = {
|
||||
success: true,
|
||||
cliente: dadosCliente.cliente,
|
||||
servicos: [],
|
||||
instancia: dadosCliente.instance
|
||||
};
|
||||
|
||||
if (servicos.success && servicos.data) {
|
||||
// Normalizar dados dos serviços
|
||||
const servicosData = servicos.data.dados || servicos.data.servicos || [];
|
||||
resultado.servicos = servicosData.map(servico => ({
|
||||
// Dados básicos do serviço
|
||||
id_servico: servico.id_servico,
|
||||
id_cliente: servico.id_cliente,
|
||||
statusFormatado: this.getStatusServicoDetalhado(servico.id_status),
|
||||
planoNome: servico.descricao || servico.servico_nome || servico.nome || 'Não informado',
|
||||
|
||||
// Dados de acesso
|
||||
login: servico.usuario || servico.login || servico.pppoe_login || '',
|
||||
senha: servico.senha || servico.pppoe_senha || '',
|
||||
ip: servico.ip || servico.ip_fixo || '',
|
||||
|
||||
// Dados financeiros
|
||||
vencimento: servico.vencimento || servico.dia_vencimento || '',
|
||||
valor: servico.servico_valor || servico.valor || servico.preco || 0,
|
||||
|
||||
// Dados técnicos
|
||||
tecnologia: servico.tecnologia || 'FTTH',
|
||||
velocidade_down: servico.servico_download || servico.velocidade_down || servico.down || '',
|
||||
velocidade_up: servico.servico_upload || servico.velocidade_up || servico.up || '',
|
||||
|
||||
// Dados FTTH específicos
|
||||
base: servico.base || '',
|
||||
id_ap: servico.id_ap || '',
|
||||
pon: servico.pon || servico.porta_pon || '',
|
||||
perfil: servico.perfil || '',
|
||||
serial: servico.serial || servico.serial_onu || servico.mac || '',
|
||||
serviceport: servico.serviceport || '',
|
||||
|
||||
// Sinais ópticos
|
||||
ultimo_rx_power: servico.ultimo_rx_power || servico.sinal_rx || servico.rx || '',
|
||||
ultimo_tx_power: servico.ultimo_tx_power || servico.sinal_tx || servico.tx || '',
|
||||
data_ultimo_sinal: servico.data_ultimo_sinal || '',
|
||||
hora_ultimo_sinal: servico.hora_ultimo_sinal || '',
|
||||
|
||||
// Status de conexão
|
||||
conexao: servico.conexao || '',
|
||||
ultimo_acesso: servico.ultimo_acesso || '',
|
||||
ultimo_ip: servico.ultimo_ip || '',
|
||||
|
||||
// Dados de instalação
|
||||
instalacao_endereco: servico.instalacao_endereco || '',
|
||||
instalacao_numero: servico.instalacao_numero || '',
|
||||
instalacao_bairro: servico.instalacao_bairro || '',
|
||||
instalacao_cidade: servico.instalacao_cidade || '',
|
||||
instalacao_uf: servico.instalacao_uf || '',
|
||||
instalacao_cep: servico.instalacao_cep || '',
|
||||
instalacao_complemento: servico.instalacao_complemento || '',
|
||||
|
||||
// Coordenadas
|
||||
latitude: servico.latitude || '',
|
||||
longitude: servico.longitude || '',
|
||||
|
||||
// Datas importantes
|
||||
data_cadastro: servico.data_cadastro || '',
|
||||
data_ativacao: servico.data_ativacao || '',
|
||||
data_bloqueio: servico.data_bloqueio || '',
|
||||
data_desativacao: servico.data_desativacao || '',
|
||||
|
||||
// Status original
|
||||
id_status: servico.id_status,
|
||||
status: servico.status
|
||||
}));
|
||||
}
|
||||
|
||||
return resultado;
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter status detalhado do serviço
|
||||
*/
|
||||
getStatusServicoDetalhado(status) {
|
||||
switch (parseInt(status)) {
|
||||
case 1: return '🟢 Ativo';
|
||||
case 2: return '🔴 Bloqueado';
|
||||
case 3: return '⚫ Desativado';
|
||||
case 4: return '🟡 Suspenso';
|
||||
case 5: return '🔵 Aguardando Ativação';
|
||||
default: return `❓ Status ${status}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter dados completos do cliente
|
||||
*/
|
||||
@@ -131,6 +245,181 @@ class TopSappManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter faturas organizadas por serviço
|
||||
*/
|
||||
async obterFaturasDetalhadas(idCliente, location = '') {
|
||||
if (!this.isReady()) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'TopSapp não está configurado'
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const credentials = this.config.getCredentialsByLocation(location);
|
||||
|
||||
// Obter faturas e serviços em paralelo
|
||||
const [faturas, servicos] = await Promise.all([
|
||||
this.api.obterFaturas(idCliente, location, credentials),
|
||||
this.api.obterServicosCliente(idCliente, location, credentials)
|
||||
]);
|
||||
|
||||
if (!faturas.success) {
|
||||
return faturas;
|
||||
}
|
||||
|
||||
if (!servicos.success) {
|
||||
return servicos;
|
||||
}
|
||||
|
||||
const servicosData = servicos.data.dados || servicos.data.servicos || [];
|
||||
const faturasData = faturas.data.dados || [];
|
||||
|
||||
// Organizar faturas por serviço
|
||||
const faturasPorServico = [];
|
||||
|
||||
for (const servico of servicosData) {
|
||||
const faturasDoServico = [];
|
||||
|
||||
// Buscar faturas relacionadas a este serviço
|
||||
for (const fatura of faturasData) {
|
||||
// Verificar se a fatura pertence a este serviço
|
||||
// Pode usar id_servico, periodo, ou outros critérios de associação
|
||||
if (fatura.id_servico === servico.id_servico ||
|
||||
fatura.codigo_cliente === servico.id_cliente) {
|
||||
|
||||
// Obter dados detalhados da fatura (linha digitável e PIX)
|
||||
const dadosFatura = await this.api.obterDadosFatura(
|
||||
fatura.id,
|
||||
fatura.codigo_cliente || idCliente,
|
||||
location,
|
||||
credentials
|
||||
);
|
||||
|
||||
const faturaDetalhada = {
|
||||
id: fatura.id,
|
||||
valor: fatura.valor,
|
||||
vencimento: fatura.vencimento,
|
||||
status: fatura.pago === 'SIM' ? 'Pago' : 'Pendente',
|
||||
descricao: fatura.historico || fatura.descricao || 'Mensalidade',
|
||||
periodo: this.formatarPeriodo(fatura.periodo_inicio, fatura.periodo_fim),
|
||||
id_servico: fatura.id_servico || servico.id_servico,
|
||||
linhaDigitavel: null,
|
||||
pixCopiaCola: null
|
||||
};
|
||||
|
||||
// Adicionar dados de pagamento se disponíveis
|
||||
if (dadosFatura.success && dadosFatura.data) {
|
||||
const dadosReturn = dadosFatura.data.fatura || dadosFatura.data.dados || dadosFatura.data;
|
||||
|
||||
if (dadosReturn.linha_digitavel) {
|
||||
faturaDetalhada.linhaDigitavel = dadosReturn.linha_digitavel;
|
||||
}
|
||||
|
||||
if (dadosReturn.qrcode) {
|
||||
faturaDetalhada.pixCopiaCola = dadosReturn.qrcode;
|
||||
}
|
||||
|
||||
// Outros campos possíveis: pix_copia_cola, codigo_pix, etc.
|
||||
if (dadosReturn.pix_copia_cola) {
|
||||
faturaDetalhada.pixCopiaCola = dadosReturn.pix_copia_cola;
|
||||
}
|
||||
|
||||
if (dadosReturn.codigo_pix) {
|
||||
faturaDetalhada.pixCopiaCola = dadosReturn.codigo_pix;
|
||||
}
|
||||
}
|
||||
|
||||
faturasDoServico.push(faturaDetalhada);
|
||||
}
|
||||
}
|
||||
|
||||
// Se o serviço tem faturas, adicionar à lista
|
||||
if (faturasDoServico.length > 0) {
|
||||
faturasPorServico.push({
|
||||
servico: {
|
||||
id: servico.id_servico,
|
||||
nome: servico.descricao || servico.plano_nome || 'Serviço',
|
||||
base: servico.base || 'Base',
|
||||
tecnologia: servico.tecnologia || 'FTTH',
|
||||
valor_mensal: servico.servico_valor || servico.valor || 0,
|
||||
vencimento_dia: servico.vencimento || 'Não definido',
|
||||
status: servico.status || 'Não definido'
|
||||
},
|
||||
faturas: faturasDoServico
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Se não conseguiu associar por serviço, criar uma entrada genérica
|
||||
if (faturasPorServico.length === 0 && faturasData.length > 0) {
|
||||
const faturasGenericas = [];
|
||||
|
||||
for (const fatura of faturasData) {
|
||||
const dadosFatura = await this.api.obterDadosFatura(
|
||||
fatura.id,
|
||||
fatura.codigo_cliente || idCliente,
|
||||
location,
|
||||
credentials
|
||||
);
|
||||
|
||||
const faturaDetalhada = {
|
||||
id: fatura.id,
|
||||
valor: fatura.valor,
|
||||
vencimento: fatura.vencimento,
|
||||
status: fatura.pago === 'SIM' ? 'Pago' : 'Pendente',
|
||||
descricao: fatura.historico || fatura.descricao || 'Mensalidade',
|
||||
periodo: this.formatarPeriodo(fatura.periodo_inicio, fatura.periodo_fim),
|
||||
linhaDigitavel: null,
|
||||
pixCopiaCola: null
|
||||
};
|
||||
|
||||
if (dadosFatura.success && dadosFatura.data) {
|
||||
const dadosReturn = dadosFatura.data.fatura || dadosFatura.data.dados || dadosFatura.data;
|
||||
|
||||
if (dadosReturn.linha_digitavel) {
|
||||
faturaDetalhada.linhaDigitavel = dadosReturn.linha_digitavel;
|
||||
}
|
||||
|
||||
if (dadosReturn.qrcode || dadosReturn.pix_copia_cola || dadosReturn.codigo_pix) {
|
||||
faturaDetalhada.pixCopiaCola = dadosReturn.qrcode || dadosReturn.pix_copia_cola || dadosReturn.codigo_pix;
|
||||
}
|
||||
}
|
||||
|
||||
faturasGenericas.push(faturaDetalhada);
|
||||
}
|
||||
|
||||
faturasPorServico.push({
|
||||
servico: {
|
||||
id: 'geral',
|
||||
nome: 'Faturas Gerais',
|
||||
base: 'Geral',
|
||||
tecnologia: 'Não especificado',
|
||||
valor_mensal: 0,
|
||||
vencimento_dia: 'Variável',
|
||||
status: 'Geral'
|
||||
},
|
||||
faturas: faturasGenericas
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
faturasPorServico,
|
||||
totalServicos: faturasPorServico.length,
|
||||
totalFaturas: faturasPorServico.reduce((total, item) => total + item.faturas.length, 0),
|
||||
instancia: faturas.instance
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter boletos do cliente
|
||||
*/
|
||||
|
||||
Reference in new issue
Block a user