Skip to content

feat(fiscal): busca de documentos em lote (NF-e e adiantamentos) com DANFE, XML e boleto - #299

Merged
malacration merged 9 commits into
malacration:masterfrom
franciscoMarcello:feat/nota-fiscal-lote
Oct 26, 2026
Merged

malacration merged 9 commits into
malacration:masterfrom
franciscoMarcello:feat/nota-fiscal-lote

Conversation

@franciscoMarcello

@franciscoMarcello franciscoMarcello commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

O que muda

Backend da tela Financeiro > Notas e XML em lote. A tela lista documentos de saída e monta um lote para baixar um zip com DANFE, XML, boleto e contrato, e para trocar a forma de pagamento em massa.

Documentos e lista

  • NF-e: OINV modelo 39 com chave na SEFAZ ("Process"). Traz o status SEFAZ e o status SAP (aberto, fechado ou devolvido; a cancelada fica de fora).
  • Adiantamento de venda futura: ODPI com U_venda_futura. Fica de fora o reconhecimento de adiantamento que o estorno cria (U_TX_DocEntryRef).
  • Chave: NF e adiantamento repetem DocEntry, então a chave é tipo + DocEntry, e os pedidos mandam docEntries e adiantamentos separados.
  • Categoria: separa a NF de venda futura do padrão antigo (CFOP 5922/6922).
  • Uma linha por parcela (INV6/DPI6): a lista traz valor original (InsTotal) e vencimento de cada parcela, e o filtro de vencimento vale por parcela.
  • Por documento: download, troca e o "adicionar todas do filtro" (porDocumento=true) usam uma linha por documento; aí o documento entra se qualquer parcela vence no intervalo.
  • Forma de pagamento: a lista traz o nome da forma (LEFT JOIN OPYM). O filtro formaPagamento aceita '' (todas), - (sem forma) ou o código. GET /formas-pagamento?todas=true inclui as formas inativas, para o filtro; sem esse parâmetro, só as ativas, que são as que a troca aceita.
  • Boleto da BankPlus, por parcela: nosso número, status (IV_IB_BoeStatus: Confirmado, Baixado, Cancelado...) e situação (IV_IB_BillOfExchangeSituation: Normal, Liquidado...). Quando a parcela tem boleto cancelado e reemitido, vale o mais recente (CTE BOLETOS com ROW_NUMBER por ID).
    • Filtro boletoStatus: -2 todos, -1 sem boleto, ou o ID do status. Na lista vale para a linha.
    • Por documento: o vencimento e o status precisam valer na mesma parcela (um EXISTS só), para o "adicionar todas" pegar exatamente os documentos que a lista mostra.
  • Origem: tudo vem do sap-odbc (odbc/nota-fiscal-lote.sql), com paginação no banco.

Download (zip)

  • DANFE e XML: vêm da TaxPlus.
  • Boleto: vem da BankPlus, é opcional e sai um por parcela.
  • Contrato (novo): PDF "Controle de Pedido" de cada contrato de venda futura dos adiantamentos, um por contrato. É o mesmo layout da tela de venda futura, gerado no servidor com Thymeleaf (templates/contrato-venda-futura.html) e iText html2pdf.
    • Dados: cabeçalho, itens e CPF/CNPJ vêm numa ida só ao sap-odbc (odbc/nota-fiscal-lote-contratos.sql).
    • Logo: vem da tela em data URI (é o logo do deploy). Só imagem, até ~512 KB, nunca URL.
    • Lote só de NF com só "contrato" marcado: é recusado, em vez de devolver um zip vazio.
  • Nomes dos arquivos: levam o nome do cliente, sem acento e até 40 caracteres. Ex.: NF501-S2-C0001-JOAO_DA_SILVA.pdf, contrato/VF412-JOAO_DA_SILVA.pdf.
  • Pendências: o que não veio vai para erros.txt ou avisos.txt.
  • Limites: até 200 documentos, 4 em paralelo e prazo próprio para o lote.

Importação da planilha do banco (novo)

  • Endpoint: POST /nota-fiscal-lote/importar-planilha, com o arquivo cru no corpo. Só lê, não grava nada.
  • Formato: a "Consulta de títulos" do BB em .xlsx, com o cabeçalho "Nosso número | Nome pagador | Nro beneficiário | Valor | Vencimento | Situação" abaixo de um bloco de totais.
  • Chave: o nosso número, sem o DV, casa com IV_IB_BillOfExchange.OurNumber, a tabela que a BankPlus grava no banco da empresa. Daí saem tipo (13 = NF, 203 = AD), DocEntry e parcela (odbc/nota-fiscal-lote-boletos.sql).
  • Conferência: "Nro beneficiário" (NF + parcela, ou DocEntry do adiantamento) e valor só conferem. O que não bate ou não dá para ler vira DIVERGENTE; nosso número desconhecido vira NAO_ENCONTRADO; documento que a tela não lista vira FORA_DA_TELA.
  • Leitor de xlsx: é próprio (PlanilhaXlsx, zip + DOM, sem dependência nova). Recusa DOCTYPE, e todas as partes do zip contam num teto de descompactado. O corpo é lido com teto de 10 MB, mesmo sem Content-Length.

Troca de forma de pagamento

  • Gravação: PATCH de PaymentMethod em Invoices e DownPayments.
  • Vínculo ao parceiro (CRD2): a forma é vinculada quando falta. No erro "Linked payment method", vincula e repete uma vez.
  • Boleto ativo: não é mexido; o resultado só avisa.
  • NF de venda futura do padrão antigo: o SAP sempre recusa ("Field cannot be updated"), então o PATCH nem é tentado e volta NAO_PERMITIDO.
  • Prazo: a troca inteira tem 300s (nota-fiscal.lote.forma-prazo-segundos) e usa um pool único do serviço, porque o RestTemplate do Service Layer não tem timeout de leitura.
  • Resultado por documento: ATUALIZADO, VINCULADO_E_ATUALIZADO, SEM_MUDANCA, ERRO, NAO_ENCONTRADO, NAO_PERMITIDO ou SEM_CONFIRMACAO. O último sai quando o SAP pode ter gravado sem confirmar; o campo pendencia marca o que ficou por conferir.

Endpoints (fora de /tax, porque o business_partner tem /tax/**/pdf). Todos no perfil nota_fiscal_lote do rules.yml:

  • GET /nota-fiscal-lote
  • GET /nota-fiscal-lote/status-sefaz
  • GET /nota-fiscal-lote/status-boleto
  • POST /nota-fiscal-lote/download
  • POST /nota-fiscal-lote/clientes/search
  • GET /nota-fiscal-lote/formas-pagamento
  • POST /nota-fiscal-lote/forma-pagamento
  • POST /nota-fiscal-lote/importar-planilha

jobs.scheduling.enabled (commit separado). O @EnableScheduling saiu do ServiceApplication e foi para um SchedulingConfig condicional. Sem a chave, tudo fica ligado como antes. Com false, uma instância local apontada para outra base não roda as rotinas agendadas.

Testes

  • Suíte do backend: 1264 verdes. Os testes novos cobrem o leitor de xlsx (zip-bomba, XXE, teto), a importação (encontrado, divergente, ilegível, reemitido, fora da tela), o PDF do contrato (HTML, logo, contrato inexistente), o zip com contrato e a troca com NF de venda futura antiga.
  • No HOMOLOG2, pelo sap-odbc:
    • A lista por parcela traz 792 linhas, contra 705 documentos na lista por documento.
    • Com filtro de vencimento em novembro, uma NF de 5 parcelas mostra só a parcela de novembro.
    • A planilha real do BB casou o título com a NF 5715, parcela 2.
    • O zip com 3 adiantamentos de 2 contratos gerou 2 PDFs, conferidos visualmente.
  • No HOMOLOG (23/10), filtros novos de forma de pagamento e boleto:
    • Não há linha duplicada pelos JOINs, e a lista responde em cerca de 1 s.
    • Em 7 combinações de vencimento e status, os documentos do modo por documento são exatamente os da lista por parcela.
  • Revisão com o Codex: em 01/10 e 06/10, e de novo em 19/10, 20/10 e 23/10, até não haver achados.

Antes de ligar em produção

  • Criar o perfil nota_fiscal_lote no Keycloak e no rules.yml do swarm, que é o que vale em produção, incluindo /nota-fiscal-lote/importar-planilha e /nota-fiscal-lote/status-boleto.
  • Conferir se o sap-odbc de produção alcança "Process", "ProcessStatus", IV_IB_BillOfExchange, IV_IB_BoeStatus, IV_IB_BillOfExchangeSituation, @AR_CONTRATO_FUTURO e @AR_CF_LINHA.
  • O PATCH real de PaymentMethod ainda não foi testado no SAP, só com mocks e consultas no HOMOLOG2. Vale testar em homologação antes.

Par no front: malacration/sap-front#168.

🤖 Generated with Claude Code

franciscoMarcello and others added 4 commits October 1, 2026 17:24
Lista as NF-e emitidas para a SEFAZ (sap-odbc, OINV x "Process"), com status
SAP/SEFAZ, chave de acesso e filtros, e baixa um zip com DANFE e XML da
TaxPlus e boletos da BankPlus. Perfil nota_fiscal_lote no rules.yml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
O @EnableScheduling sai do ServiceApplication para um SchedulingConfig condicional.
Sem a chave, tudo continua ligado como antes; com jobs.scheduling.enabled=false, uma
instancia local apontada para outra base nao roda as rotinas agendadas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…DANFE, XML e boleto

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	src/main/resources/application.properties.sample
#	src/main/resources/rules.yml
…trato de venda futura

- Lista com uma linha por parcela (valor original e vencimento da parcela); download, troca
  e "adicionar todas" (porDocumento) seguem um por documento.
- POST /nota-fiscal-lote/importar-planilha: monta o lote a partir da planilha de titulos do
  BB (.xlsx), casando o nosso numero com os boletos da BankPlus (IV_IB_BillOfExchange).
  Leitor xlsx proprio, com teto de tamanho e sem DOCTYPE.
- PDF do contrato de venda futura ("Controle de Pedido") no zip, um por contrato dos
  adiantamentos; logo vem da tela.
- Nome do cliente nos nomes de arquivo do zip.
- Troca de forma: NF de venda futura do padrao antigo (CFOP 5922/6922) nem tenta o PATCH
  e volta NAO_PERMITIDO.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a lista do lote

- Forma de pagamento: nome da forma na lista (JOIN OPYM) e filtro :formaPagamento
  ('' todas, '-' sem forma); /formas-pagamento?todas=true inclui inativas para o filtro
  (a troca segue so com ativas).
- Boleto da BankPlus por parcela: nosso numero, status (IV_IB_BoeStatus) e situacao
  (IV_IB_BillOfExchangeSituation) do boleto mais recente da parcela; filtro :boletoStatus
  (-2 todos, -1 sem boleto). Por documento, vencimento e status valem na mesma parcela.
- GET /nota-fiscal-lote/status-boleto no perfil nota_fiscal_lote.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@malacration
malacration deployed to homologation October 26, 2026 20:29 — with GitHub Actions Active
# Conflicts:
#	src/main/kotlin/br/andrew/sap/services/cadastro/BusinessPartnersService.kt
@malacration
malacration merged commit e9c1668 into malacration:master Oct 26, 2026
1 check passed

This branch was successfully deployed

1 active deployment
homologation — 383e5b0b Deployed Oct 26, 2026 by franciscoMarcello via teste #630
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants