From ba8ef2994b6a00659ada843bb3fcd8874713120d Mon Sep 17 00:00:00 2001 From: Rodrigo Bertoldo de Sousa Martins Date: Wed, 14 Oct 2026 16:41:07 -0300 Subject: [PATCH] fix10 --- src/sgp/api.js | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/sgp/api.js b/src/sgp/api.js index 01230d4..02e700d 100644 --- a/src/sgp/api.js +++ b/src/sgp/api.js @@ -453,7 +453,7 @@ class SgpAPI { async obterFaturas(idCliente, location = '', credentials, idServico = null) { return this._wrap(credentials, async () => { - const params = { status: 'abertos' }; + const params = { status: 'abertos', ordenar: 'data_vencimento', ordenar_ordem: 'asc' }; if (idCliente) params.cliente_id = parseInt(idCliente, 10); if (idServico) params.contrato = parseInt(idServico, 10); @@ -466,12 +466,15 @@ class SgpAPI { const lote = data.titulos || []; lote.forEach(t => { this.tituloCache.set(String(t.id), { at: Date.now(), titulo: t }); - dados.push(this._mapTitulo(t, idCliente)); + dados.push({ ...this._mapTitulo(t, idCliente), _venc: t.dataVencimento || '' }); }); const total = data.paginacao?.total ?? lote.length; offset += lote.length; if (lote.length === 0 || offset >= total) break; } + // Garante ordem crescente de vencimento (AAAA-MM-DD ordena como texto) + dados.sort((a, b) => String(a._venc).localeCompare(String(b._venc))); + dados.forEach(d => delete d._venc); return { dados }; }); } @@ -493,10 +496,53 @@ class SgpAPI { return this._wrap(credentials, async () => { const titulo = await this._obterTitulo(idFatura, credentials); const f = this._mapTitulo(titulo, idCliente); + + // A listagem de títulos nem sempre traz o PIX; nesse caso gera/consulta pelo endpoint de PIX + if (!f.qrcode && titulo.clienteContrato) { + f.qrcode = await this._obterPix(titulo, credentials); + if (f.qrcode) titulo.codigoPix = f.qrcode; // fica no cache + } return { fatura: f }; }); } + /** + * Gera (ou consulta) o PIX copia e cola do título. + */ + async _obterPix(titulo, credentials) { + try { + const data = await this._request(credentials, 'POST', `/api/ura/pagamento/pix/${titulo.id}`, { + contrato: titulo.clienteContrato + }); + if (process.env.SGP_DEBUG === 'true') { + console.log('🧪 [SGP] Resposta do PIX:', JSON.stringify(data)); + } + return this._extrairPix(data); + } catch (error) { + logger.warn('SGP: não foi possível gerar o PIX', { titulo: titulo.id, error: error.message }); + return ''; + } + } + + /** + * Procura o código copia e cola (EMV, começa com "000201") na resposta, qualquer que seja o campo. + */ + _extrairPix(data, nivel = 0) { + if (!data || nivel > 4) return ''; + if (typeof data === 'string') return /^0002\d{2}/.test(data.trim()) ? data.trim() : ''; + if (typeof data !== 'object') return ''; + const preferidos = ['codigoPix', 'codigo_pix', 'qrcode', 'pix_copia_cola', 'copia_cola', 'copiaecola', 'emv', 'brcode', 'payload', 'pix']; + for (const k of preferidos) { + const v = this._extrairPix(data[k], nivel + 1); + if (v) return v; + } + for (const k of Object.keys(data)) { + const v = this._extrairPix(data[k], nivel + 1); + if (v) return v; + } + return ''; + } + /** * Baixa o boleto em PDF e devolve em Base64 (mesmo contrato do TopSapp). */