sgp - pedro afonso

This commit is contained in:
Rodrigo Bertoldo de Sousa Martins committed 2026-10-14 15:10:30 -03:00
1 parent ddf426fd5f
commit 828b6e7209
6 files changed
+865 -98

No files matched your search

+15 -3
View File
@@ -38,9 +38,21 @@ JUNIPER_PEDRO_AFONSO_PASS=
TOPSAPP_GUARAI_USER=seu_usuario_guarai TOPSAPP_GUARAI_USER=seu_usuario_guarai
TOPSAPP_GUARAI_PASS=sua_senha_guarai TOPSAPP_GUARAI_PASS=sua_senha_guarai
# Configurações TopSapp - Instância Pedro Afonso # Configurações SGP - Pedro Afonso (substitui o TopSapp nesta cidade)
TOPSAPP_PEDRO_AFONSO_USER=seu_usuario_pedro_afonso # Autenticação: app + token (Sistema -> Ferramentas -> Painel Admin -> Tokens)
TOPSAPP_PEDRO_AFONSO_PASS=sua_senha_pedro_afonso # ou, alternativamente, usuário + senha do SGP (Basic Auth)
SGP_PEDRO_AFONSO_URL=https://seu-sgp.exemplo.com.br
SGP_PEDRO_AFONSO_APP=nome_da_aplicacao
SGP_PEDRO_AFONSO_TOKEN=token_da_aplicacao
#SGP_PEDRO_AFONSO_USER=
#SGP_PEDRO_AFONSO_PASS=
# Opcional: id da OLT no SGP -> id_ap legado (2 = Pedro Afonso, 4 = Canavieiras).
# Se omitido, é deduzido pelo nome da OLT no SGP.
#SGP_PEDRO_AFONSO_OLT_MAP=12:2,15:4
# Opcional: ids de status de contrato do SGP (padrão: 1 = Ativo, 4 = Suspenso)
#SGP_STATUS_ATIVO=1
#SGP_STATUS_BLOQUEADO=4
#SGP_INSECURE_TLS=false
# ========================================================== # ==========================================================
# Configurações GenieACS (TR-069 ACS) # Configurações GenieACS (TR-069 ACS)
+4
View File
@@ -6899,6 +6899,10 @@ Digite /help para ver todos os comandos disponíveis!
dadosSuporte.outrasInformacoes = `Criado via Bot Telegram\nData original: ${dados.dataHoraTexto}`; dadosSuporte.outrasInformacoes = `Criado via Bot Telegram\nData original: ${dados.dataHoraTexto}`;
// Usados pelo SGP (técnico responsável e agendamento são definidos na abertura do chamado)
dadosSuporte.idTecnico = dados.tecnicoSelecionado?.id;
dadosSuporte.dataHoraAgendamento = dados.dataHora;
const resultado = await this.topsapp.adicionarSuporte(dadosSuporte, location); const resultado = await this.topsapp.adicionarSuporte(dadosSuporte, location);
if (resultado.success) { if (resultado.success) {
+718
View File
@@ -0,0 +1,718 @@
const axios = require('axios');
const https = require('https');
const logger = require('../utils/logger');
/**
* Cliente da API do SGP (https://documenter.getpostman.com/view/6682240/2sB34hHg2V).
*
* Expõe a mesma superfície que o TopSappAPI usa no TopSappManager e devolve os
* dados no mesmo formato ({ success, data: { dados: [...] }, instance }), de
* modo que o restante do bot não precise saber qual sistema está por trás.
*
* Convenções de mapeamento:
* - idCliente -> id do cliente no SGP
* - id_servico -> id do CONTRATO no SGP (bloqueio/liberação são feitos por contrato)
* - id da fatura -> id do título no SGP
*/
// Status de contrato no SGP (documentação pública)
const SGP_STATUS_ATIVO = parseInt(process.env.SGP_STATUS_ATIVO || '1', 10);
const SGP_STATUS_BLOQUEADO = parseInt(process.env.SGP_STATUS_BLOQUEADO || '4', 10); // Suspenso
class SgpAPI {
constructor() {
this.instances = {
pedro_afonso: {
key: 'pedro_afonso',
name: 'Pedro Afonso (SGP)',
cities: ['pedro afonso', 'pedro_afonso']
}
};
this.httpsAgent = new https.Agent({
rejectUnauthorized: process.env.SGP_INSECURE_TLS !== 'true'
});
this.tituloCache = new Map(); // id título -> { at, titulo }
this.tecnicoCache = new Map(); // id técnico -> username
console.log('🔗 SgpAPI inicializado');
}
// ------------------------------------------------------------------ infra
getInstanceByLocation() {
return this.instances.pedro_afonso;
}
getInstances() {
return Object.values(this.instances);
}
clearExpiredSessions() {
const now = Date.now();
for (const [id, entry] of this.tituloCache.entries()) {
if (now - entry.at > 2 * 60 * 1000) this.tituloCache.delete(id);
}
}
_name(credentials) {
return this.getInstanceByLocation().name;
}
/**
* Requisição genérica ao SGP. Autentica com token+app (corpo/query) ou Basic Auth.
*/
async _request(credentials, method, path, params = {}) {
if (!credentials || !credentials.baseURL) {
throw new Error('SGP não configurado (URL ausente)');
}
const clean = {};
Object.keys(params).forEach(k => {
if (params[k] !== undefined && params[k] !== null && params[k] !== '') clean[k] = params[k];
});
const config = {
method,
url: `${credentials.baseURL.replace(/\/$/, '')}${path}`,
httpsAgent: this.httpsAgent,
timeout: 30000
};
if (credentials.app && credentials.token) {
clean.app = credentials.app;
clean.token = credentials.token;
} else if (credentials.usuario && credentials.senha) {
config.auth = { username: credentials.usuario, password: credentials.senha };
} else {
throw new Error('SGP sem credenciais (app/token ou usuário/senha)');
}
if (method === 'GET') {
config.params = clean;
} else {
const body = new URLSearchParams();
Object.keys(clean).forEach(k => body.append(k, String(clean[k])));
config.data = body.toString();
config.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
}
const response = await axios(config);
return response.data;
}
/**
* Envolve uma chamada no formato { success, data, instance } / { success:false, error }
*/
async _wrap(credentials, fn) {
try {
const data = await fn();
return { success: true, data, instance: this._name(credentials) };
} catch (error) {
const body = error.response?.data;
const msg = (body && (body.msg || body.mensagem || body.detail || body.message || body.erro))
|| error.message;
logger.warn('Erro na API SGP', { error: msg, status: error.response?.status });
console.error(`❌ Erro SGP: ${msg}`);
return { success: false, error: typeof msg === 'string' ? msg : JSON.stringify(msg), instance: this._name(credentials) };
}
}
// --------------------------------------------------------------- helpers
_formatarData(data) {
if (!data) return '';
const m = String(data).match(/^(\d{4})-(\d{2})-(\d{2})/);
return m ? `${m[3]}/${m[2]}/${m[1]}` : String(data);
}
_statusId(texto) {
const t = String(texto || '').toLowerCase()
.normalize('NFD').replace(/[̀-ͯ]/g, '').trim();
if (/^ativo/.test(t)) return 1;
if (/suspens|bloquead/.test(t)) return 2;
if (/cancelad|inativ|desativ/.test(t)) return 3;
return 5; // aguardando / inviabilidade / outros
}
_textoStatus(idStatus) {
switch (idStatus) {
case 1: return 'liberado';
case 2: return 'bloqueado';
case 3: return 'desativado';
default: return 'desconhecido';
}
}
/** id_ap legado (2 = Pedro Afonso, 4 = Canavieiras) a partir da OLT do SGP */
_idApLegado(onu) {
if (!onu) return '';
const mapa = {};
(process.env.SGP_PEDRO_AFONSO_OLT_MAP || '').split(',').forEach(par => {
const [sgpId, legado] = par.split(':').map(s => s && s.trim());
if (sgpId && legado) mapa[sgpId] = legado;
});
if (onu.olt_id !== undefined && mapa[String(onu.olt_id)]) return mapa[String(onu.olt_id)];
const nome = String(onu.olt_nome || '').toLowerCase();
if (/canavieiras/.test(nome)) return '4';
if (/pedro|afonso/.test(nome)) return '2';
return onu.olt_id !== undefined ? String(onu.olt_id) : '';
}
_clienteId(c) {
return c.id ?? c.clienteId ?? c.cliente_id ?? c.idCliente ?? null;
}
_mapCliente(c) {
const contratos = c.contratos || [];
const ativo = contratos.some(ct => this._statusId(ct.status) === 1);
const contatos = c.contatos || c.telefones || [];
const primeiroContato = Array.isArray(contatos) && contatos.length
? (typeof contatos[0] === 'string' ? contatos[0] : (contatos[0].contato || contatos[0].numero || ''))
: '';
const end = c.endereco || {};
return {
id: this._clienteId(c),
nome: c.nome,
cpf: c.cpfcnpj || '',
cpfCnpj: c.cpfcnpj || '',
email: c.email || (Array.isArray(c.emails) ? c.emails[0] : '') || '',
telefone: c.telefone || c.celular || primeiroContato,
celular: c.celular || c.telefone || primeiroContato,
endereco: end.logradouro || '',
numero: end.numero || '',
bairro: end.bairro || '',
cidade: end.cidade || '',
uf: end.uf || '',
cep: end.cep || '',
complemento: end.complemento || '',
observacoes: c.observacao_cliente || '',
data_cadastro: c.dataCadastro || '',
status: ativo ? 1 : 0,
statusOriginal: contratos.map(ct => ct.status).join(', '),
id_status: ativo ? 1 : 0
};
}
_mapServicos(c) {
const idCliente = this._clienteId(c);
const end = c.endereco || {};
const out = [];
(c.contratos || []).forEach(ct => {
const idStatus = this._statusId(ct.status);
(ct.servicos && ct.servicos.length ? ct.servicos : [{}]).forEach(srv => {
const onu = srv.onu || {};
const conexao = onu.conexao || srv.conexao || {};
const e = srv.endereco || end;
out.push({
id: ct.contrato,
id_servico: ct.contrato,
servico_id: srv.id,
id_cliente: idCliente,
idCliente,
descricao: srv.plano?.descricao || srv.tipo || 'Serviço',
nome: srv.plano?.descricao || srv.tipo || 'Serviço',
plano_nome: srv.plano?.descricao || '',
tipo_servico: srv.tipo || '',
id_status: idStatus,
status: this._textoStatus(idStatus),
motivo_status: ct.motivo_status || '',
data_cadastro: ct.dataCadastro || '',
usuario: srv.login || onu.login || '',
login: srv.login || onu.login || '',
pppoe_login: srv.login || '',
senha: srv.senha || '',
ip: srv.ip || '',
mac: srv.mac || '',
// Dados do cliente (o TopSapp os devolvia embutidos no serviço)
instalacao_nome: c.nome,
cpf: c.cpfcnpj || '',
instalacao_endereco: e.logradouro || '',
instalacao_numero: e.numero || '',
instalacao_bairro: e.bairro || '',
instalacao_cidade: e.cidade || '',
instalacao_uf: e.uf || '',
instalacao_cep: e.cep || '',
instalacao_complemento: e.complemento || '',
latitude: e.latitude || '',
longitude: e.longitude || '',
// FTTH
tecnologia: 'FTTH',
base: onu.olt_nome || '',
id_ap: this._idApLegado(onu),
id_olt_sgp: onu.olt_id,
pon: onu.pon !== undefined ? `${onu.slot ?? ''}/${onu.pon}` : '',
perfil: '',
serial: onu.serial || '',
serviceport: '',
ultimo_rx_power: onu.rx || '',
ultimo_tx_power: onu.tx || '',
data_ultimo_sinal: '',
hora_ultimo_sinal: '',
conexao: conexao.status || '',
ultimo_acesso: conexao.data_conexao || '',
ultimo_ip: conexao.ip || '',
dhcp_username: '',
dhcp_agent_circuit_id: '',
dhcp_client_id: '',
dhcp_liberar: undefined
});
});
});
return out;
}
async _listarClientes(credentials, params) {
const todos = [];
let offset = 0;
for (let pagina = 0; pagina < 20; pagina++) {
const data = await this._request(credentials, 'POST', '/api/ura/clientes/', {
omitir_titulos: 1,
limit: 100,
offset,
...params
});
const lote = data.clientes || [];
todos.push(...lote);
const total = data.paginacao?.total ?? lote.length;
offset += lote.length;
if (lote.length === 0 || offset >= total) break;
}
return todos;
}
// --------------------------------------------------------------- clientes
async buscarCliente(query, location = '', credentials) {
const instance = this.getInstanceByLocation();
try {
const q = String(query || '').trim();
const params = {};
const digitos = q.replace(/\D/g, '');
if (/^\d+$/.test(q) && q.length !== 11 && q.length !== 14) {
params.cliente_id = parseInt(q, 10);
} else if (digitos.length === 11 || digitos.length === 14) {
params.cpfcnpj = digitos;
} else {
params.cliente_nome = q;
}
console.log(`🔍 [SGP] Buscando cliente:`, params);
const clientes = await this._listarClientes(credentials, params);
return {
success: true,
clientes: clientes.map(c => this._mapCliente(c)),
instance: instance.name
};
} catch (error) {
console.error('❌ [SGP] Erro na busca de cliente:', error.message);
return { success: false, error: error.response?.data?.msg || error.message, instance: instance.name };
}
}
async obterServicosCliente(idCliente, location = '', credentials) {
return this._wrap(credentials, async () => {
const id = parseInt(idCliente, 10);
if (isNaN(id)) throw new Error(`ID do cliente inválido: ${idCliente}`);
const clientes = await this._listarClientes(credentials, { cliente_id: id });
const dados = [];
clientes.forEach(c => dados.push(...this._mapServicos({ ...c, id: this._clienteId(c) ?? id })));
return { dados };
});
}
async obterDadosCompletosCliente(idCliente, location = '', credentials) {
const instance = this.getInstanceByLocation();
try {
const id = parseInt(idCliente, 10);
const clientes = await this._listarClientes(credentials, { cliente_id: id });
if (!clientes.length) {
return { success: false, error: 'Cliente não encontrado', instance: instance.name };
}
const c = { ...clientes[0], id: this._clienteId(clientes[0]) ?? id };
const cliente = this._mapCliente(c);
const primeiro = this._mapServicos(c)[0] || {};
cliente.latitude = primeiro.latitude || '';
cliente.longitude = primeiro.longitude || '';
return { success: true, cliente, instance: instance.name, fonte: 'sgp' };
} catch (error) {
return { success: false, error: error.response?.data?.msg || error.message, instance: instance.name };
}
}
async obterDadosCliente(idCliente, location = '', credentials) {
const r = await this.obterDadosCompletosCliente(idCliente, location, credentials);
if (!r.success) return r;
return { success: true, data: { clientes: [r.cliente] }, instance: r.instance };
}
interpretarStatusCliente(cliente) {
return cliente.status === 1 || cliente.id_status === 1 ? 1 : 0;
}
interpretarStatusServico(servico) {
return servico.id_status || this._statusId(servico.status);
}
obterTextoStatus(idStatus) {
return this._textoStatus(parseInt(idStatus, 10));
}
// ----------------------------------------------------------------- faturas
_mapTitulo(t, idCliente = null) {
const pago = t.status === 'pago' || !!t.dataPagamento;
return {
id: t.id,
valor: t.valorCorrigido ?? t.valor,
valor_original: t.valor,
vencimento: this._formatarData(t.dataVencimento),
pago: pago ? 'SIM' : 'NAO',
historico: t.demonstrativo || `Fatura ${t.numeroDocumento || t.id}`,
descricao: t.demonstrativo || 'Mensalidade',
periodo_inicio: '',
periodo_fim: '',
codigo_cliente: idCliente,
id_servico: t.clienteContrato,
numero_documento: t.numeroDocumento,
link: t.link,
linha_digitavel: t.linhaDigitavel || '',
codigo_barras: t.codigoBarras || '',
qrcode: t.codigoPix || ''
};
}
async obterFaturas(idCliente, location = '', credentials, idServico = null) {
return this._wrap(credentials, async () => {
const params = { status: 'abertos' };
if (idCliente) params.cliente_id = parseInt(idCliente, 10);
if (idServico) params.contrato = parseInt(idServico, 10);
const dados = [];
let offset = 0;
for (let pagina = 0; pagina < 20; pagina++) {
const data = await this._request(credentials, 'POST', '/api/ura/titulos/', {
...params, limit: 250, offset
});
const lote = data.titulos || [];
lote.forEach(t => {
this.tituloCache.set(String(t.id), { at: Date.now(), titulo: t });
dados.push(this._mapTitulo(t, idCliente));
});
const total = data.paginacao?.total ?? lote.length;
offset += lote.length;
if (lote.length === 0 || offset >= total) break;
}
return { dados };
});
}
async _obterTitulo(idFatura, credentials) {
const cached = this.tituloCache.get(String(idFatura));
if (cached && Date.now() - cached.at < 2 * 60 * 1000) return cached.titulo;
const data = await this._request(credentials, 'POST', '/api/ura/titulos/', {
titulo_id: parseInt(idFatura, 10)
});
const titulo = (data.titulos || [])[0];
if (!titulo) throw new Error('Fatura não encontrada');
this.tituloCache.set(String(idFatura), { at: Date.now(), titulo });
return titulo;
}
async obterDadosFatura(idFatura, idCliente, location = '', credentials) {
return this._wrap(credentials, async () => {
const titulo = await this._obterTitulo(idFatura, credentials);
const f = this._mapTitulo(titulo, idCliente);
return { fatura: f };
});
}
/**
* Baixa o boleto em PDF e devolve em Base64 (mesmo contrato do TopSapp).
*/
async obterBoletoBase64(idFatura, location = '', credentials) {
return this._wrap(credentials, async () => {
const titulo = await this._obterTitulo(idFatura, credentials);
let link = null;
try {
const seg = await this._request(credentials, 'POST', '/api/ura/fatura2via/', {
contrato: titulo.clienteContrato,
numero_documento: titulo.numeroDocumento,
link_pdf: 1,
nao_gerar_os: 1
});
link = (seg.links || []).find(l => String(l.fatura) === String(idFatura))?.link || seg.link;
} catch (error) {
logger.warn('SGP fatura2via falhou, usando link do título', { error: error.message });
}
link = link || titulo.link;
if (!link) throw new Error('Boleto sem link disponível');
const resp = await axios.get(link, {
responseType: 'arraybuffer',
httpsAgent: this.httpsAgent,
timeout: 30000
});
const tipo = String(resp.headers['content-type'] || '');
if (!/pdf/i.test(tipo)) throw new Error(`O link do boleto não retornou PDF (${tipo || 'desconhecido'}): ${link}`);
return { arquivo: Buffer.from(resp.data).toString('base64'), link };
});
}
convertBase64ToPDF(base64String, filename = 'boleto.pdf') {
try {
const base64Data = base64String.replace(/^data:application\/pdf;base64,/, '');
return { success: true, buffer: Buffer.from(base64Data, 'base64'), filename, mimeType: 'application/pdf' };
} catch (error) {
return { success: false, error: error.message };
}
}
// ------------------------------------------------------ bloqueio/liberação
async _alterarStatusContrato(idContrato, status, credentials) {
return this._wrap(credentials, () => this._request(credentials, 'POST', '/api/ura/contrato/status/edit/', {
contrato: parseInt(idContrato, 10),
status
}));
}
async alterarStatusServico(idServico, idCliente, status, location = '', credentials) {
// status no formato TopSapp: 1 = ativo, 2 = bloqueado
return this._alterarStatusContrato(idServico, status === 1 ? SGP_STATUS_ATIVO : SGP_STATUS_BLOQUEADO, credentials);
}
async bloquearServico(idServico, idCliente, location = '', credentials) {
return this._alterarStatusContrato(idServico, SGP_STATUS_BLOQUEADO, credentials);
}
async liberarServico(idServico, idCliente, location = '', credentials) {
return this._alterarStatusContrato(idServico, SGP_STATUS_ATIVO, credentials);
}
// ----------------------------------------------------------------- suporte
async obterTecnicos(location = '', credentials) {
return this._wrap(credentials, async () => {
const lista = await this._request(credentials, 'POST', '/api/ura/tecnicos/');
(lista || []).forEach(t => this.tecnicoCache.set(String(t.id), t.username));
return { dados: (lista || []).map(t => ({ id: t.id, nome: t.nome, username: t.username })) };
});
}
/** Equivalente aos "operadores" do TopSapp: o bot só usa id_tecnico e tecnico */
async obterOperadores(location = '', credentials) {
return this._wrap(credentials, async () => {
const lista = await this._request(credentials, 'POST', '/api/ura/tecnicos/');
(lista || []).forEach(t => this.tecnicoCache.set(String(t.id), t.username));
return { dados: (lista || []).map(t => ({ id: t.id, id_tecnico: t.id, tecnico: t.nome, username: t.username })) };
});
}
async obterFuncionarios(location = '', credentials) {
return this.obterTecnicos(location, credentials);
}
async obterSuporteCategorias(location = '', credentials) {
return this._wrap(credentials, async () => {
const lista = await this._request(credentials, 'GET', '/api/os/ocorrencia/tipo/list/');
// "ocorrenciatipo" do chamado usa o código do tipo
return { dados: (lista || []).map(t => ({ id: t.codigo ?? t.id, descricao: t.descricao })) };
});
}
async obterSuporteTiposAtendimento(location = '', credentials) {
return this._wrap(credentials, async () => {
const lista = await this._request(credentials, 'GET', '/api/ura/ocorrencia/metodo/list/');
return { dados: (lista || []).map(m => ({ id: m.codigo, descricao: m.descricao })) };
});
}
/**
* Abre chamado (ocorrência + O.S.). idServico = contrato.
*/
async adicionarSuporte(dadosSuporte, location = '', credentials) {
return this._wrap(credentials, async () => {
const params = {
contrato: parseInt(dadosSuporte.idServico, 10),
conteudo: dadosSuporte.descricao,
conteudolimpo: 1,
ocorrenciatipo: dadosSuporte.idCategoria,
observacao: dadosSuporte.outrasInformacoes,
contato_nome: dadosSuporte.contato,
contato_telefone: dadosSuporte.contatoTelefone
};
if (dadosSuporte.idTecnico) {
const username = this.tecnicoCache.get(String(dadosSuporte.idTecnico));
if (username) params.responsavel = username;
}
if (dadosSuporte.dataHoraAgendamento) {
const d = new Date(dadosSuporte.dataHoraAgendamento);
if (!isNaN(d.getTime())) {
const p = n => String(n).padStart(2, '0');
params.data_hora_agendamento = `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
}
}
const data = await this._request(credentials, 'POST', '/api/ura/chamado/', params);
if (data.status !== undefined && Number(data.status) !== 1) {
throw new Error(data.msg || 'Não foi possível abrir o chamado');
}
return { protocolo: data.protocolo, ...data };
});
}
/**
* O técnico já é definido na abertura do chamado (campo "responsavel").
*/
async encaminharSuporte(idSuporte, idTecnico, descricao, location = '', credentials) {
return { success: true, data: { msg: 'Técnico definido na abertura do chamado' }, instance: this._name(credentials) };
}
async obterSuportesAbertos(idCliente, location = '', credentials) {
return this._wrap(credentials, async () => {
const lista = await this._request(credentials, 'POST', '/api/os/list/', {
cliente_id: parseInt(idCliente, 10)
});
const dados = (lista || [])
.filter(os => Number(os.os_status) !== 1 && !os.os_data_finalizacao)
.map(os => ({
id: os.os_id,
protocolo: os.os_protocolo,
defeito: os.os_conteudo || os.os_motivo_descricao || '',
endereco: os.endereco_logradouro || '',
numero: os.endereco_numero || '',
bairro: os.endereco_bairro || '',
cidade: os.endereco_cidade || '',
data: os.os_data_agendamento || os.os_data_cadastro || ''
}));
return { dados };
});
}
async finalizarSuporte(idSuporte, descricao, location = '', credentials) {
return this._wrap(credentials, () => {
const d = new Date();
const p = n => String(n).padStart(2, '0');
const agora = `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
return this._request(credentials, 'POST', `/api/os/update/id/${parseInt(idSuporte, 10)}/`, {
os_status: 1,
os_servicoprestado: descricao,
os_data_finalizacao: agora
});
});
}
// -------------------------------------------------------------------- FTTH
/**
* Lista OLTs no formato que o fluxo de vínculo de ONU espera (nome/ssid/portas/perfis).
* O SGP não exige porta/perfil para vincular uma ONU já autorizada, então
* são devolvidos valores neutros apenas para o fluxo seguir.
*/
async obterOlts(location = '', credentials) {
return this._wrap(credentials, async () => {
const lista = await this._request(credentials, 'GET', '/api/fttx/olt/list/');
return {
dados: (lista || []).map(o => ({
id: o.id,
nome: o.name,
ssid: o.host || '',
portas: [{ id: 0, alias: 'SGP' }],
perfis: [{ id: 0, identificacao: 'SGP' }]
}))
};
});
}
/**
* Vincula uma ONU a um serviço: localiza a ONU pelo serial no SGP e associa ao login do contrato.
*/
async atualizarFTTH(idCliente, idServico, ftthData, location = '', credentials) {
return this._wrap(credentials, async () => {
const serial = String(ftthData.serial || '').toUpperCase();
if (!serial) throw new Error('Serial da ONU não informado');
const servicos = await this.obterServicosCliente(idCliente, location, credentials);
if (!servicos.success) throw new Error(servicos.error);
const srv = servicos.data.dados.find(s => String(s.id_servico) === String(idServico));
if (!srv) throw new Error(`Contrato #${idServico} não encontrado para o cliente`);
if (!srv.login) throw new Error('Contrato sem login de serviço para vincular a ONU');
const onus = await this._request(credentials, 'GET', '/api/fttx/onu/list/');
const onu = (onus || []).find(o => String(o.phy_addr || '').toUpperCase() === serial);
if (!onu) {
throw new Error(`ONU ${serial} não está cadastrada/autorizada no SGP. Autorize-a no SGP e tente novamente.`);
}
return this._request(credentials, 'POST', `/api/fttx/onu/${onu.id}/edit/`, {
onu_update: 'service',
service: srv.login
});
});
}
// ------------------------------------------------------------------ RADIUS
/**
* Sessões PPPoE (equivalente a ObterClientesServicosAcessos).
* params: { status: 'on' } ou { dataInicial, dataFinal } (DD/MM/AAAA)
*/
async obterAcessos(params, instance, credentials) {
return this._wrap(credentials, async () => {
const query = { limit: 1000 };
if (params.status === 'on') query.online = 1;
const iso = d => {
const m = String(d || '').match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
return m ? `${m[3]}-${m[2]}-${m[1]}` : null;
};
const ini = iso(params.dataInicial);
const fim = iso(params.dataFinal);
const sessoes = [];
let offset = 0;
for (let pagina = 0; pagina < 20; pagina++) {
const data = await this._request(credentials, 'POST', '/ws/radius/radacct/list/all/', { ...query, offset });
const lote = data.result || [];
lote.forEach(item => {
(item.radacct || []).forEach(r => {
const inicio = String(r.acctstarttime || '');
if (ini && fim && inicio) {
const dia = inicio.slice(0, 10);
const termDia = r.acctstoptime ? String(r.acctstoptime).slice(0, 10) : '9999-12-31';
if (dia > fim || termDia < ini) return;
}
sessoes.push({
id_cliente: -1,
usuario: r.username || item.pppoe_login,
ip: r.framedipaddress || item.ip || '',
inicio,
termino: r.acctstoptime || '',
nas_ip: r.nasipaddress || '',
nome: item.nome
});
});
});
const total = data.paggination?.total ?? lote.length;
offset += lote.length;
if (lote.length === 0 || offset >= total) break;
}
return { dados: sessoes };
});
}
}
module.exports = SgpAPI;
-10
View File
@@ -9,11 +9,6 @@ class TopSappAPI {
baseURL: 'https://131.0.192.6:9910', baseURL: 'https://131.0.192.6:9910',
name: 'Guarai e Colmeia', name: 'Guarai e Colmeia',
cities: ['guarai', 'colmeia'] cities: ['guarai', 'colmeia']
},
'pedro_afonso': {
baseURL: 'https://45.189.140.18:9910',
name: 'Pedro Afonso',
cities: ['pedro afonso', 'pedro_afonso']
} }
}; };
@@ -37,11 +32,6 @@ class TopSappAPI {
getInstanceByLocation(location = '') { getInstanceByLocation(location = '') {
const loc = location.toLowerCase(); const loc = location.toLowerCase();
// Pedro Afonso
if (loc.includes('pedro') || loc.includes('afonso')) {
return this.instances.pedro_afonso;
}
// Guarai e Colmeia (padrão) // Guarai e Colmeia (padrão)
return this.instances.guarai_colmeia; return this.instances.guarai_colmeia;
} }
+38 -11
View File
@@ -10,11 +10,16 @@ class TopSappConfig {
identificador: process.env.TOPSAPP_GUARAI_ID || 'BotTelegram', identificador: process.env.TOPSAPP_GUARAI_ID || 'BotTelegram',
idUsuario: process.env.TOPSAPP_GUARAI_USERID || '1' idUsuario: process.env.TOPSAPP_GUARAI_USERID || '1'
}, },
};
// Pedro Afonso migrou do TopSapp para o SGP
this.sgpCredentials = {
pedro_afonso: { pedro_afonso: {
usuario: process.env.TOPSAPP_PEDRO_AFONSO_USER || '', baseURL: process.env.SGP_PEDRO_AFONSO_URL || '',
senha: process.env.TOPSAPP_PEDRO_AFONSO_PASS || '', app: process.env.SGP_PEDRO_AFONSO_APP || '',
identificador: process.env.TOPSAPP_PEDRO_AFONSO_ID || 'BotTelegram', token: process.env.SGP_PEDRO_AFONSO_TOKEN || '',
idUsuario: process.env.TOPSAPP_PEDRO_AFONSO_USERID || '1' usuario: process.env.SGP_PEDRO_AFONSO_USER || '',
senha: process.env.SGP_PEDRO_AFONSO_PASS || ''
} }
}; };
@@ -41,6 +46,14 @@ class TopSappConfig {
return true; return true;
} }
/**
* SGP configurado quando há URL e (app+token ou usuário+senha)
*/
isSgpConfigured(key = 'pedro_afonso') {
const c = this.sgpCredentials[key];
return !!(c && c.baseURL && ((c.app && c.token) || (c.usuario && c.senha)));
}
/** /**
* Obter credenciais para uma instância * Obter credenciais para uma instância
*/ */
@@ -62,20 +75,28 @@ class TopSappConfig {
* Obter credenciais por localização * Obter credenciais por localização
*/ */
getCredentialsByLocation(location = '') { getCredentialsByLocation(location = '') {
const loc = location.toLowerCase(); if (this.usesSgp(location)) {
return this.sgpCredentials.pedro_afonso;
if (loc.includes('pedro') || loc.includes('afonso')) {
return this.credentials.pedro_afonso;
} }
return this.credentials.guarai_colmeia; return this.credentials.guarai_colmeia;
} }
/**
* Pedro Afonso é atendido pelo SGP; as demais localidades pelo TopSapp
*/
usesSgp(location = '') {
const loc = String(location || '').toLowerCase();
return loc.includes('pedro') || loc.includes('afonso');
}
/** /**
* Verificar se o TopSapp está configurado * Verificar se o TopSapp está configurado
*/ */
isReady() { isReady(location) {
return this.isConfigured; if (location === undefined) {
return this.isConfigured || this.isSgpConfigured();
}
return this.usesSgp(location) ? this.isSgpConfigured() : this.isConfigured;
} }
/** /**
@@ -93,6 +114,12 @@ class TopSappConfig {
}; };
} }
status.pedro_afonso_sgp = {
configured: this.isSgpConfigured(),
usuario: this.sgpCredentials.pedro_afonso.baseURL ? '✅' : '❌',
senha: (this.sgpCredentials.pedro_afonso.token || this.sgpCredentials.pedro_afonso.senha) ? '✅' : '❌'
};
return status; return status;
} }
} }
+90 -74
View File
@@ -1,4 +1,5 @@
const TopSappAPI = require('./api'); const TopSappAPI = require('./api');
const SgpAPI = require('../sgp/api');
const TopSappConfig = require('./config'); const TopSappConfig = require('./config');
const logger = require('../utils/logger'); const logger = require('../utils/logger');
const fs = require('fs').promises; const fs = require('fs').promises;
@@ -6,14 +7,16 @@ const path = require('path');
class TopSappManager { class TopSappManager {
constructor() { constructor() {
this.api = new TopSappAPI(); this.topsappApi = new TopSappAPI();
this.sgpApi = new SgpAPI();
this.config = new TopSappConfig(); this.config = new TopSappConfig();
logger.info('TopSappManager inicializado'); logger.info('TopSappManager inicializado');
// Limpar sessões expiradas a cada 10 minutos // Limpar sessões expiradas a cada 10 minutos
setInterval(() => { setInterval(() => {
this.api.clearExpiredSessions(); this.topsappApi.clearExpiredSessions();
this.sgpApi.clearExpiredSessions();
}, 10 * 60 * 1000); }, 10 * 60 * 1000);
console.log('🎯 TopSappManager inicializado'); console.log('🎯 TopSappManager inicializado');
@@ -22,8 +25,16 @@ class TopSappManager {
/** /**
* Verificar se o TopSapp está configurado e pronto * Verificar se o TopSapp está configurado e pronto
*/ */
isReady() { isReady(location) {
return this.config.isReady(); return this.config.isReady(location);
}
/**
* Backend (TopSapp ou SGP) responsável pela localidade.
* Pedro Afonso usa o SGP; as demais localidades usam o TopSapp.
*/
_api(location = '') {
return this.config.usesSgp(location) ? this.sgpApi : this.topsappApi;
} }
/** /**
@@ -37,7 +48,7 @@ class TopSappManager {
* Buscar cliente por nome, CPF ou ID * Buscar cliente por nome, CPF ou ID
*/ */
async buscarCliente(query, location = '') { async buscarCliente(query, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado. Verifique as credenciais no arquivo .env' error: 'TopSapp não está configurado. Verifique as credenciais no arquivo .env'
@@ -46,7 +57,7 @@ class TopSappManager {
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
const result = await this.api.buscarCliente(query, location, credentials); const result = await this._api(location).buscarCliente(query, location, credentials);
if (result.success && result.clientes) { if (result.success && result.clientes) {
return { return {
@@ -70,7 +81,7 @@ class TopSappManager {
* Obter dados cadastrais completos do cliente * Obter dados cadastrais completos do cliente
*/ */
async obterDadosCadastrais(idCliente, location = '') { async obterDadosCadastrais(idCliente, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -84,8 +95,8 @@ class TopSappManager {
// Buscar dados completos do cliente e serviços em paralelo // Buscar dados completos do cliente e serviços em paralelo
const [dadosCliente, servicos] = await Promise.all([ const [dadosCliente, servicos] = await Promise.all([
this.api.obterDadosCompletosCliente(idCliente, location, credentials), this._api(location).obterDadosCompletosCliente(idCliente, location, credentials),
this.api.obterServicosCliente(idCliente, location, credentials) this._api(location).obterServicosCliente(idCliente, location, credentials)
]); ]);
if (!dadosCliente.success) { if (!dadosCliente.success) {
@@ -204,7 +215,7 @@ class TopSappManager {
* Obter dados completos do cliente * Obter dados completos do cliente
*/ */
async obterDadosCompletos(idCliente, location = '') { async obterDadosCompletos(idCliente, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -216,9 +227,9 @@ class TopSappManager {
// Buscar dados do cliente, serviços e faturas em paralelo // Buscar dados do cliente, serviços e faturas em paralelo
const [dadosCliente, servicos, faturas] = await Promise.all([ const [dadosCliente, servicos, faturas] = await Promise.all([
this.api.obterDadosCliente(idCliente, location, credentials), this._api(location).obterDadosCliente(idCliente, location, credentials),
this.api.obterServicosCliente(idCliente, location, credentials), this._api(location).obterServicosCliente(idCliente, location, credentials),
this.api.obterFaturas(idCliente, location, credentials) this._api(location).obterFaturas(idCliente, location, credentials)
]); ]);
const resultado = { const resultado = {
@@ -255,7 +266,7 @@ class TopSappManager {
* Obter faturas organizadas por serviço * Obter faturas organizadas por serviço
*/ */
async obterFaturasDetalhadas(idCliente, location = '') { async obterFaturasDetalhadas(idCliente, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -266,7 +277,7 @@ class TopSappManager {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
// Buscar serviços do cliente primeiro // Buscar serviços do cliente primeiro
const servicos = await this.api.obterServicosCliente(idCliente, location, credentials); const servicos = await this._api(location).obterServicosCliente(idCliente, location, credentials);
if (!servicos.success) { if (!servicos.success) {
return servicos; return servicos;
@@ -281,7 +292,7 @@ class TopSappManager {
const faturasDoServico = []; const faturasDoServico = [];
// Buscar faturas filtradas pelo id_servico diretamente na API // Buscar faturas filtradas pelo id_servico diretamente na API
const faturasServico = await this.api.obterFaturas( const faturasServico = await this._api(location).obterFaturas(
idCliente, location, credentials, servico.id_servico idCliente, location, credentials, servico.id_servico
); );
@@ -291,7 +302,7 @@ class TopSappManager {
for (const fatura of faturasData) { for (const fatura of faturasData) {
// Obter dados detalhados da fatura (linha digitável e PIX) // Obter dados detalhados da fatura (linha digitável e PIX)
const dadosFatura = await this.api.obterDadosFatura( const dadosFatura = await this._api(location).obterDadosFatura(
fatura.id, fatura.id,
fatura.codigo_cliente || idCliente, fatura.codigo_cliente || idCliente,
location, location,
@@ -366,7 +377,7 @@ class TopSappManager {
* Obter boletos do cliente * Obter boletos do cliente
*/ */
async obterBoletos(idCliente, location = '') { async obterBoletos(idCliente, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -375,7 +386,7 @@ class TopSappManager {
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
const faturas = await this.api.obterFaturas(idCliente, location, credentials); const faturas = await this._api(location).obterFaturas(idCliente, location, credentials);
if (!faturas.success) { if (!faturas.success) {
return faturas; return faturas;
@@ -385,7 +396,7 @@ class TopSappManager {
for (const fatura of faturas.data.dados || []) { for (const fatura of faturas.data.dados || []) {
// Obter dados da fatura (linha digitável) // Obter dados da fatura (linha digitável)
const dadosFatura = await this.api.obterDadosFatura(fatura.id, fatura.codigo_cliente, location, credentials); const dadosFatura = await this._api(location).obterDadosFatura(fatura.id, fatura.codigo_cliente, location, credentials);
const boleto = { const boleto = {
id: fatura.id, id: fatura.id,
@@ -430,7 +441,7 @@ class TopSappManager {
* Gerar PDF do boleto * Gerar PDF do boleto
*/ */
async gerarBoletoPDF(idFatura, location = '') { async gerarBoletoPDF(idFatura, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -439,14 +450,14 @@ class TopSappManager {
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
const boletoBase64 = await this.api.obterBoletoBase64(idFatura, location, credentials); const boletoBase64 = await this._api(location).obterBoletoBase64(idFatura, location, credentials);
if (!boletoBase64.success) { if (!boletoBase64.success) {
return boletoBase64; return boletoBase64;
} }
// Converter Base64 para PDF // Converter Base64 para PDF
const pdfResult = this.api.convertBase64ToPDF( const pdfResult = this._api(location).convertBase64ToPDF(
boletoBase64.data.arquivo, boletoBase64.data.arquivo,
`boleto_${idFatura}.pdf` `boleto_${idFatura}.pdf`
); );
@@ -507,7 +518,7 @@ class TopSappManager {
* Bloquear cliente * Bloquear cliente
*/ */
async bloquearCliente(idCliente, location = '') { async bloquearCliente(idCliente, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -518,7 +529,7 @@ class TopSappManager {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
// Obter serviços do cliente // Obter serviços do cliente
const servicos = await this.api.obterServicosCliente(idCliente, location, credentials); const servicos = await this._api(location).obterServicosCliente(idCliente, location, credentials);
if (!servicos.success) { if (!servicos.success) {
return servicos; return servicos;
@@ -529,7 +540,7 @@ class TopSappManager {
// Bloquear todos os serviços ativos // Bloquear todos os serviços ativos
for (const servico of servicos.data.dados || []) { for (const servico of servicos.data.dados || []) {
if (servico.status === 1 || servico.estado === 'liberado') { // Apenas serviços ativos if (servico.status === 1 || servico.estado === 'liberado') { // Apenas serviços ativos
const resultado = await this.api.bloquearServico(servico.id, idCliente, location, credentials); const resultado = await this._api(location).bloquearServico(servico.id, idCliente, location, credentials);
resultados.push({ resultados.push({
servicoId: servico.id, servicoId: servico.id,
descricao: servico.descricao || servico.nome, descricao: servico.descricao || servico.nome,
@@ -557,7 +568,7 @@ class TopSappManager {
* Liberar cliente * Liberar cliente
*/ */
async liberarCliente(idCliente, location = '') { async liberarCliente(idCliente, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -568,7 +579,7 @@ class TopSappManager {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
// Obter serviços do cliente // Obter serviços do cliente
const servicos = await this.api.obterServicosCliente(idCliente, location, credentials); const servicos = await this._api(location).obterServicosCliente(idCliente, location, credentials);
if (!servicos.success) { if (!servicos.success) {
return servicos; return servicos;
@@ -579,7 +590,7 @@ class TopSappManager {
// Liberar todos os serviços bloqueados // Liberar todos os serviços bloqueados
for (const servico of servicos.data.dados || []) { for (const servico of servicos.data.dados || []) {
if (servico.status === 2 || servico.estado === 'bloqueado') { // Apenas serviços bloqueados if (servico.status === 2 || servico.estado === 'bloqueado') { // Apenas serviços bloqueados
const resultado = await this.api.liberarServico(servico.id, idCliente, location, credentials); const resultado = await this._api(location).liberarServico(servico.id, idCliente, location, credentials);
resultados.push({ resultados.push({
servicoId: servico.id, servicoId: servico.id,
descricao: servico.descricao || servico.nome, descricao: servico.descricao || servico.nome,
@@ -613,7 +624,7 @@ class TopSappManager {
tipoIdCliente: typeof idCliente tipoIdCliente: typeof idCliente
}); });
if (!this.isReady()) { if (!this.isReady(location)) {
logger.warn('TopSapp não está configurado'); logger.warn('TopSapp não está configurado');
return { return {
success: false, success: false,
@@ -625,7 +636,7 @@ class TopSappManager {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
logger.debug('Credenciais obtidas', { location, hasCredentials: !!credentials }); logger.debug('Credenciais obtidas', { location, hasCredentials: !!credentials });
const servicos = await this.api.obterServicosCliente(idCliente, location, credentials); const servicos = await this._api(location).obterServicosCliente(idCliente, location, credentials);
logger.debug('Resposta da API obterServicosCliente', { logger.debug('Resposta da API obterServicosCliente', {
success: servicos.success, success: servicos.success,
dataLength: servicos.data?.dados?.length, dataLength: servicos.data?.dados?.length,
@@ -779,7 +790,7 @@ class TopSappManager {
* Bloquear serviço específico * Bloquear serviço específico
*/ */
async bloquearServicoEspecifico(idCliente, idServico, location = '') { async bloquearServicoEspecifico(idCliente, idServico, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -788,7 +799,7 @@ class TopSappManager {
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
const resultado = await this.api.bloquearServico(idServico, idCliente, location, credentials); const resultado = await this._api(location).bloquearServico(idServico, idCliente, location, credentials);
return { return {
...resultado, ...resultado,
@@ -807,7 +818,7 @@ class TopSappManager {
* Liberar serviço específico * Liberar serviço específico
*/ */
async liberarServicoEspecifico(idCliente, idServico, location = '') { async liberarServicoEspecifico(idCliente, idServico, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -816,7 +827,7 @@ class TopSappManager {
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
const resultado = await this.api.liberarServico(idServico, idCliente, location, credentials); const resultado = await this._api(location).liberarServico(idServico, idCliente, location, credentials);
return { return {
...resultado, ...resultado,
@@ -835,7 +846,7 @@ class TopSappManager {
* Alterar status de um serviço específico * Alterar status de um serviço específico
*/ */
async alterarStatusServico(idServico, acao, location = '') { async alterarStatusServico(idServico, acao, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -859,7 +870,7 @@ class TopSappManager {
} }
// Primeiro, obter informações do serviço para saber o idCliente // Primeiro, obter informações do serviço para saber o idCliente
const servicos = await this.api.obterServicosCliente(null, location, credentials); const servicos = await this._api(location).obterServicosCliente(null, location, credentials);
if (!servicos.success) { if (!servicos.success) {
return { return {
@@ -889,7 +900,7 @@ class TopSappManager {
acao acao
}); });
const resultado = await this.api.alterarStatusServico(idServico, idCliente, status, location, credentials); const resultado = await this._api(location).alterarStatusServico(idServico, idCliente, status, location, credentials);
return { return {
...resultado, ...resultado,
@@ -909,7 +920,7 @@ class TopSappManager {
* Obter tipos de atendimento do suporte * Obter tipos de atendimento do suporte
*/ */
async obterSuporteTiposAtendimento(location = '') { async obterSuporteTiposAtendimento(location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -918,7 +929,7 @@ class TopSappManager {
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
const result = await this.api.obterSuporteTiposAtendimento(location, credentials); const result = await this._api(location).obterSuporteTiposAtendimento(location, credentials);
return result; return result;
@@ -934,7 +945,7 @@ class TopSappManager {
* Obter técnicos disponíveis * Obter técnicos disponíveis
*/ */
async obterTecnicos(location = '') { async obterTecnicos(location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -943,7 +954,7 @@ class TopSappManager {
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
const result = await this.api.obterTecnicos(location, credentials); const result = await this._api(location).obterTecnicos(location, credentials);
return result; return result;
@@ -959,7 +970,7 @@ class TopSappManager {
* Obter operadores disponíveis * Obter operadores disponíveis
*/ */
async obterOperadores(location = '') { async obterOperadores(location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -968,7 +979,7 @@ class TopSappManager {
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
const result = await this.api.obterOperadores(location, credentials); const result = await this._api(location).obterOperadores(location, credentials);
return result; return result;
@@ -984,7 +995,7 @@ class TopSappManager {
* Obter funcionários disponíveis * Obter funcionários disponíveis
*/ */
async obterFuncionarios(location = '') { async obterFuncionarios(location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -993,7 +1004,7 @@ class TopSappManager {
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
const result = await this.api.obterFuncionarios(location, credentials); const result = await this._api(location).obterFuncionarios(location, credentials);
return result; return result;
@@ -1009,7 +1020,7 @@ class TopSappManager {
* Obter categorias de suporte * Obter categorias de suporte
*/ */
async obterSuporteCategorias(location = '') { async obterSuporteCategorias(location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -1018,7 +1029,7 @@ class TopSappManager {
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
const result = await this.api.obterSuporteCategorias(location, credentials); const result = await this._api(location).obterSuporteCategorias(location, credentials);
return result; return result;
@@ -1034,7 +1045,7 @@ class TopSappManager {
* Adicionar novo suporte * Adicionar novo suporte
*/ */
async adicionarSuporte(dadosSuporte, location = '') { async adicionarSuporte(dadosSuporte, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -1043,7 +1054,7 @@ class TopSappManager {
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
const result = await this.api.adicionarSuporte(dadosSuporte, location, credentials); const result = await this._api(location).adicionarSuporte(dadosSuporte, location, credentials);
if (result.success) { if (result.success) {
console.log(`✅ Suporte criado com sucesso. Protocolo: ${result.data.protocolo || 'N/A'}`); console.log(`✅ Suporte criado com sucesso. Protocolo: ${result.data.protocolo || 'N/A'}`);
@@ -1063,13 +1074,13 @@ class TopSappManager {
* Encaminhar suporte para um técnico * Encaminhar suporte para um técnico
*/ */
async encaminharSuporte(idSuporte, idTecnico, descricao, location = '') { async encaminharSuporte(idSuporte, idTecnico, descricao, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { success: false, error: 'TopSapp não está configurado' }; return { success: false, error: 'TopSapp não está configurado' };
} }
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
return await this.api.encaminharSuporte(idSuporte, idTecnico, descricao, location, credentials); return await this._api(location).encaminharSuporte(idSuporte, idTecnico, descricao, location, credentials);
} catch (error) { } catch (error) {
return { success: false, error: error.message }; return { success: false, error: error.message };
} }
@@ -1079,7 +1090,7 @@ class TopSappManager {
* Obter instâncias disponíveis * Obter instâncias disponíveis
*/ */
getInstances() { getInstances() {
return this.api.getInstances(); return [...this.topsappApi.getInstances(), ...this.sgpApi.getInstances()];
} }
/** /**
@@ -1155,7 +1166,7 @@ class TopSappManager {
* Obter faturas do cliente * Obter faturas do cliente
*/ */
async obterFaturas(idCliente, location = '') { async obterFaturas(idCliente, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -1164,7 +1175,7 @@ class TopSappManager {
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
return await this.api.obterFaturas(idCliente, location, credentials); return await this._api(location).obterFaturas(idCliente, location, credentials);
} catch (error) { } catch (error) {
return { return {
success: false, success: false,
@@ -1177,7 +1188,7 @@ class TopSappManager {
* Obter dados completos de uma fatura (linha digitável, PIX, etc) * Obter dados completos de uma fatura (linha digitável, PIX, etc)
*/ */
async obterDadosFatura(idFatura, location = '') { async obterDadosFatura(idFatura, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { return {
success: false, success: false,
error: 'TopSapp não está configurado' error: 'TopSapp não está configurado'
@@ -1187,8 +1198,13 @@ class TopSappManager {
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
// No SGP o título é consultado diretamente pelo id
if (this.config.usesSgp(location)) {
return await this.sgpApi.obterDadosFatura(idFatura, null, location, credentials);
}
// Primeiro obter a lista de faturas para pegar o idCliente // Primeiro obter a lista de faturas para pegar o idCliente
const faturas = await this.api.obterFaturas(null, location, credentials); const faturas = await this._api(location).obterFaturas(null, location, credentials);
if (!faturas.success || !faturas.data || !faturas.data.dados) { if (!faturas.success || !faturas.data || !faturas.data.dados) {
return { return {
@@ -1210,7 +1226,7 @@ class TopSappManager {
const idCliente = fatura.codigo_cliente || fatura.id_cliente; const idCliente = fatura.codigo_cliente || fatura.id_cliente;
// Obter dados completos da fatura // Obter dados completos da fatura
return await this.api.obterDadosFatura(idFatura, idCliente, location, credentials); return await this._api(location).obterDadosFatura(idFatura, idCliente, location, credentials);
} catch (error) { } catch (error) {
return { return {
@@ -1223,36 +1239,36 @@ class TopSappManager {
* Obter suportes abertos de um cliente * Obter suportes abertos de um cliente
*/ */
async obterSuportesAbertos(idCliente, location = '') { async obterSuportesAbertos(idCliente, location = '') {
if (!this.isReady()) return { success: false, error: 'TopSapp não configurado' }; if (!this.isReady(location)) return { success: false, error: 'TopSapp não configurado' };
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
return await this.api.obterSuportesAbertos(idCliente, location, credentials); return await this._api(location).obterSuportesAbertos(idCliente, location, credentials);
} }
/** /**
* Finalizar suporte * Finalizar suporte
*/ */
async finalizarSuporte(idSuporte, descricao, location = '') { async finalizarSuporte(idSuporte, descricao, location = '') {
if (!this.isReady()) return { success: false, error: 'TopSapp não configurado' }; if (!this.isReady(location)) return { success: false, error: 'TopSapp não configurado' };
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
return await this.api.finalizarSuporte(idSuporte, descricao, location, credentials); return await this._api(location).finalizarSuporte(idSuporte, descricao, location, credentials);
} }
/** /**
* Obter lista de OLTs cadastradas no TopSapp * Obter lista de OLTs cadastradas no TopSapp
*/ */
async obterOlts(location = '') { async obterOlts(location = '') {
if (!this.isReady()) return { success: false, error: 'TopSapp não configurado' }; if (!this.isReady(location)) return { success: false, error: 'TopSapp não configurado' };
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
return await this.api.obterOlts(location, credentials); return await this._api(location).obterOlts(location, credentials);
} }
/** /**
* Atualizar dados FTTH de um serviço (vincular ONU a cliente/serviço) * Atualizar dados FTTH de um serviço (vincular ONU a cliente/serviço)
*/ */
async atualizarFTTH(idCliente, idServico, ftthData, location = '') { async atualizarFTTH(idCliente, idServico, ftthData, location = '') {
if (!this.isReady()) return { success: false, error: 'TopSapp não configurado' }; if (!this.isReady(location)) return { success: false, error: 'TopSapp não configurado' };
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
return await this.api.atualizarFTTH(idCliente, idServico, ftthData, location, credentials); return await this._api(location).atualizarFTTH(idCliente, idServico, ftthData, location, credentials);
} }
/** /**
@@ -1276,7 +1292,7 @@ class TopSappManager {
* Determina automaticamente o prefixo do Circuit-ID pela OLT/base * Determina automaticamente o prefixo do Circuit-ID pela OLT/base
*/ */
async mudarParaIPoE(idCliente, idServico, location = '') { async mudarParaIPoE(idCliente, idServico, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { success: false, error: 'TopSapp não está configurado' }; return { success: false, error: 'TopSapp não está configurado' };
} }
@@ -1284,7 +1300,7 @@ class TopSappManager {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
// Obter serviços do cliente para descobrir serial e OLT/base // Obter serviços do cliente para descobrir serial e OLT/base
const servicosResult = await this.api.obterServicosCliente(idCliente, location, credentials); const servicosResult = await this._api(location).obterServicosCliente(idCliente, location, credentials);
if (!servicosResult.success) { if (!servicosResult.success) {
return { success: false, error: 'Não foi possível obter serviços do cliente' }; return { success: false, error: 'Não foi possível obter serviços do cliente' };
} }
@@ -1334,13 +1350,13 @@ class TopSappManager {
* Usa status:'on' — somente sessões ativas têm o campo `ip` preenchido pelo RADIUS. * Usa status:'on' — somente sessões ativas têm o campo `ip` preenchido pelo RADIUS.
*/ */
async buscarConexaoAtivaPorIp(privateIP, location = '') { async buscarConexaoAtivaPorIp(privateIP, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { success: false, error: 'TopSapp não está configurado' }; return { success: false, error: 'TopSapp não está configurado' };
} }
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
const instance = this.api.getInstanceByLocation(location); const instance = this._api(location).getInstanceByLocation(location);
const result = await this.api.obterAcessos({ status: 'on' }, instance, credentials); const result = await this._api(location).obterAcessos({ status: 'on' }, instance, credentials);
if (!result.success) return result; if (!result.success) return result;
@@ -1371,16 +1387,16 @@ class TopSappManager {
* @param {string} location * @param {string} location
*/ */
async buscarSessaoPorHorario(privateIP, dataInicial, dataFinal, horaRef = null, location = '') { async buscarSessaoPorHorario(privateIP, dataInicial, dataFinal, horaRef = null, location = '') {
if (!this.isReady()) { if (!this.isReady(location)) {
return { success: false, error: 'TopSapp não está configurado' }; return { success: false, error: 'TopSapp não está configurado' };
} }
try { try {
const credentials = this.config.getCredentialsByLocation(location); const credentials = this.config.getCredentialsByLocation(location);
const instance = this.api.getInstanceByLocation(location); const instance = this._api(location).getInstanceByLocation(location);
const params = {}; const params = {};
if (dataInicial) params.dataInicial = dataInicial; if (dataInicial) params.dataInicial = dataInicial;
if (dataFinal) params.dataFinal = dataFinal; if (dataFinal) params.dataFinal = dataFinal;
const result = await this.api.obterAcessos(params, instance, credentials); const result = await this._api(location).obterAcessos(params, instance, credentials);
if (!result.success) return result; if (!result.success) return result;