06 — CNAB (Remessa e Retorno)¶
CNAB (Centro Nacional de Automação Bancária) é o padrão FEBRABAN para troca de arquivos entre empresa e banco. A PyCobrança implementa os dois layouts de uso geral na cobrança registrada:
| Layout | Tamanho do registro | Uso típico | Status |
|---|---|---|---|
| CNAB 240 | 240 posições | Padrão FEBRABAN mais recente, estrutura em lotes. | ✅ |
| CNAB 400 | 400 posições | Layout legado por banco, ainda muito usado. | ✅ |
| CNAB 444 | 444 posições | Variante do Itaú — o 400 acrescido de 44 posições de mensagem. | 🚧 |
CNAB 444 ainda não é implementado
O 444 é uma variante do Itaú: o registro de 400 posições acrescido de 44 para mensagem no boleto. Não há suporte na PyCobrança — nem na remessa, nem no retorno. Está registrado no roadmap e entra sob o mesmo critério dos demais layouts: manual oficial com exemplo numérico validável, e comparação byte a byte contra vetor de referência.
Status (Fase 2): remessa CNAB 400 implementada para 12 bancos (Itaú, Bradesco, Banco do Brasil, Santander, Sicoob, Unicred, Banrisul, Banco do Nordeste, BRB/DCB, Citibank, CrediSIS e C6) e CNAB 240 para 7 bancos (Ailos, Banco do Brasil, Caixa, Santander, Sicoob, Sicredi e Unicred), todas validadas byte a byte contra vetores de referência (fixtures congeladas em
tests/fixtures/; verdocs/bancos/).Bolepix na remessa (Fase 4): com um
PagamentoPix, a remessa acrescenta o registro tipo 8 (CNAB 400: Itaú, Bradesco, C6, Santander) ou o segmento Y-03 (CNAB 240: Banco do Brasil, Caixa, Sicoob) após cada título — classesRemessa*Pix, validadas byte a byte.
Conceitos¶
- Remessa: arquivo gerado pela empresa e enviado ao banco solicitando registro/baixa/alteração
de títulos. Fluxo:
Boleto[] → registros → string do arquivo. - Retorno: arquivo devolvido pelo banco com o resultado do processamento (liquidação,
rejeição, baixa). Fluxo:
string do arquivo → registros → list[dict].
Estrutura do subsistema¶
cnab/
├── pagamento.py # título a registrar (campos + formatadores CNAB)
├── formatacao.py # format_size / format_valor / remover_acentos (fiéis ao Ruby)
├── cnab400/
│ ├── base.py # header/detalhe/trailer + gera_arquivo (400 posições)
│ └── <banco>.py # RemessaXxx400 (12 bancos)
├── cnab240/
│ ├── base.py # header arq/lote + segmentos P/Q/R + trailers (240 posições)
│ └── <banco>.py # RemessaXxx240 (7 bancos)
└── retorno/
├── base.py # RegistroRetorno + extração posicional + motivo
├── cnab400.py # mapas por banco (header = código nas pos. 76–78)
├── cnab240.py # combinação dos segmentos T/U (header = código nas pos. 0–2)
└── ocorrencias.py # rótulos legíveis dos códigos de ocorrência (convenience)
Retorno — status (Fase 3)¶
Implementado e validado campo a campo contra vetores de referência para 11 arquivos
.RET. CNAB 400: Itaú, Bradesco, Banco do Brasil, Santander (com campos PIX), Banco do Nordeste, Banrisul, CrediSIS, C6, Unicred e BRB. CNAB 240: base/Caixa, Santander, Ailos, Sicredi e Sicoob (dois segmentos, T = dados gerais e U = valores, combinados em um registro).Retorno.ler(caminho)detecta o layout pelo tamanho do registro e o banco pelo header.
Layouts declarativos¶
Cada registro é descrito por uma tabela de campos posicionais (início, fim, tipo, formato, default). Isso permite testar posições isoladamente e reusar definições entre bancos.
# Exemplo conceitual de definição posicional (layout declarativo)
HEADER_ARQUIVO_240 = [
Campo("banco", 1, 3, tipo="num"),
Campo("lote", 4, 7, tipo="num", default="0000"),
Campo("tipo_registro", 8, 8, tipo="num", default="0"),
# ...
Campo("nome_empresa", 73, 102, tipo="alfa"),
# ...
]
Estrutura CNAB 240 (em lotes)¶
Header de Arquivo (registro 0)
Header de Lote (registro 1)
Detalhe Segmento P (registro 3) ─┐ título
Detalhe Segmento Q (registro 3) ─┤ sacado
Detalhe Segmento R (registro 3) ─┘ multa/desconto/PIX
Trailer de Lote (registro 5)
Trailer de Arquivo (registro 9)
Estrutura CNAB 400 (por registro)¶
Remessa — API¶
Não há uma classe genérica: cada banco e layout tem a sua, porque as posições e as regras
mudam de um para o outro. O nome segue Remessa<Banco><Layout>, com o sufixo Pix na variante
que inclui o segmento de Bolepix.
from datetime import date
from pycobranca.cnab import Pagamento, RemessaItau400
pagamentos = [
Pagamento(
nosso_numero="12345678",
numero="DOC0001",
valor=199.90,
data_vencimento=date(2026, 8, 15),
data_emissao=date(2026, 7, 23),
documento_sacado="52998224725",
nome_sacado="Cliente Final da Silva",
endereco_sacado="Rua das Flores, 100",
bairro_sacado="Centro",
cep_sacado="30110000",
cidade_sacado="Belo Horizonte",
uf_sacado="MG",
),
]
remessa = RemessaItau400(
empresa_mae="Empresa Exemplo LTDA",
documento_cedente="11222333000181",
agencia="0057",
conta_corrente="12345",
digito_conta="7",
carteira="109",
data_geracao=date(2026, 7, 23),
pagamentos=pagamentos,
)
arquivo = remessa.gera_arquivo() # -> str, com os registros separados por \n
gera_arquivo() devolve o conteúdo; gravar em disco é responsabilidade de quem chama —
a biblioteca não escreve arquivos.
Para validar os encargos antes de gerar, chame Pagamento.validar() em cada item (verifica
valor > 0 e a coerência de mora, multa e desconto) e Remessa.validar() no conjunto.
As classes disponíveis saem do próprio pacote:
import pycobranca.cnab as cnab
sorted(n for n in dir(cnab) if n.startswith("Remessa"))
# ['RemessaAilos240', 'RemessaBancoBrasil240', 'RemessaBancoBrasil240Pix', ...]
Quais bancos têm cada layout está em 05 — Bancos Suportados.
Exemplos executáveis em examples/.
Encargos (juros/mora, multa, desconto, IOF, abatimento)¶
Cada encargo do Pagamento é um trio código/tipo → valor → data (a data é opcional):
| Encargo | Código/tipo | Valor | Data |
|---|---|---|---|
| Multa | codigo_multa (0=isento, 1=valor fixo, 2=percentual) |
percentual_multa (%) |
data_multa |
| Juros/Mora | tipo_mora (1=valor/dia, 2=taxa mensal %, 3=isento) |
valor_mora (R$, tipo 1) · percentual_mora (%, tipo 2) |
data_mora |
| Desconto 1º/2º/3º | cod_desconto / cod_segundo_desconto / cod_terceiro_desconto |
valor_desconto / valor_segundo_desconto / valor_terceiro_desconto |
data_desconto / data_segundo_desconto / data_terceiro_desconto |
| IOF | — | valor_iof |
— |
| Abatimento | — | valor_abatimento |
— |
Todos os campos são opcionais e têm default neutro ("0"/0.0/None); sem informá-los, o
arquivo sai idêntico ao anterior (encargos zerados).
Semântica por layout:
- CNAB 240 — segmento P:
tipo_mora+ data de mora + valor/percentual de mora (percentual quandotipo_mora == "2", conforme FEBRABAN "Taxa Mensal") + 1º desconto + IOF + abatimento. Segmento R: 2º e 3º desconto + multa (código + data + percentual). As datas de mora e multa usam o campo informado; na ausência, caem para o vencimento (Caixa e Sicredi/Unicred usam vencimento + 1 dia), como prevê o padrão FEBRABAN. - CNAB 400 — a multa é sempre percentual (
percentual_multa); a mora é valor ao dia (valor_mora). Larguras variam por banco (ex.: Sicoob 6, Banrisul 12, Brasília/DCB 14). Só o C6 usadata_morano detalhe; os demais 400 não carregam data de mora.
Observação: no padrão FEBRABAN não existe "valor de multa" monetário — a multa é sempre percentual. O método
Pagamento.formata_valor_multaé mantido apenas por compatibilidade e é um alias deformata_percentual_multa.
Suporte por banco¶
O Pagamento sempre aceita os campos; eles entram no arquivo onde o layout tem posição.
CNAB 240 — suporte completo e uniforme (segmentos P/R):
| Banco (CNAB 240) | Mora (valor/%) | Multa | Desc. 1º | Desc. 2º | Desc. 3º | IOF | Abat. |
|---|---|---|---|---|---|---|---|
| Banco do Brasil (001) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Caixa (104) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Santander (033) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Sicoob (756) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Sicredi (748) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Unicred (136) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Ailos (085) | ✅ | ✅⁴ | ✅ | ✅ | ✅ | ✅ | ✅ |
CNAB 400 — varia por layout do banco:
| Banco (CNAB 400) | Mora | Multa | Desc. 1º | Desc. 2º | IOF | Abat. |
|---|---|---|---|---|---|---|
| Santander (033) | ✅ | ✅ | ✅ | 📅² | ✅ | ✅ |
| Bradesco (237) | ✅ | ✅ | ✅ | — | ✅ | ✅ |
| Sicoob (756) | ✅ | ✅ | ✅ | — | ✅ | ✅ |
| Banrisul (041) | ✅ | ✅ | ✅ | — | ✅ | ✅ |
| Banco do Nordeste (004) | ✅ | ✅ | ✅ | — | ✅ | ✅ |
| C6 (336) | ✅ | ✅ | ✅ | — | — | ✅ |
| Unicred (136) | ✅ | ✅ | ✅ | — | — | ✅ |
| CrediSIS (097) | ✅ | ✅ | ✅³ | — | — | — |
| Itaú (341) | ✅ | 📝¹ | ✅ | — | ✅ | ✅ |
| Banco do Brasil (001) | ✅ | 📝¹ | ✅ | — | ✅ | ✅ |
| Citibank (745) | ✅ | — | ✅ | ✅ | ✅ | ✅ |
| BRB/Brasília (070) | ✅ | — | ✅ | — | — | ✅ |
Legenda: ✅ campo posicional na remessa · — sem campo no layout · Desc. 3º apenas no CNAB 240. 📝¹ Itaú/BB (400): multa vai por instrução (código), não como percentual posicional. 📅² Santander (400): 2º desconto só a data. ³ CrediSIS: 1º desconto sem campo de data. ⁴ Ailos (240): segmento R (multa + 2º/3º desconto) emitido só quando há multa. Banestes (021), HSBC (399) e Safra (422) só emitem boleto (sem remessa CNAB).
Retorno — API¶
from pycobranca.cnab.retorno import Retorno
retorno = Retorno.ler("retorno.ret") # layout e banco detectados automaticamente
for registro in retorno.registros:
print(
registro.nosso_numero,
registro.codigo_ocorrencia,
retorno.descricao_ocorrencia(registro),
registro.valor_recebido,
registro.data_credito,
)
# Serialização JSON-friendly para conciliação
dados = retorno.to_dict() # list[dict], nulos removidos
Validação:
Retorno.lerlevantaRetornoInvalidopara um arquivo vazio ou sem header de banco reconhecível (em vez de devolver lista vazia). Ver contrato de erros.
Os valores são devolvidos como strings cruas do arquivo (ex.: valor_recebido
"0000000003790" = R$ 37,90; datas no formato do banco), preservando fidelidade ao retorno; a
interpretação numérica/monetária fica a cargo do consumidor (aplicação).
Agrupamento determinístico (remessa em lote)¶
Ao gerar remessa a partir de vários títulos, o agrupamento é determinístico: um arquivo CNAB nunca mistura banco, layout, convênio, carteira ou conta incompatíveis. Quando a entrada contém títulos heterogêneos, ela é separada automaticamente em sublotes compatíveis, cada um gerando seu próprio arquivo. Os totais de header/trailer são validados antes de disponibilizar o arquivo, o conteúdo é guardado de forma imutável para auditoria e é possível reprocessar apenas o sublote com erro. O fluxo assíncrono desse agrupamento está detalhado em 12 — Processamento em Lote.
Mapeamento de ocorrências¶
O parsing traduz códigos numéricos de ocorrência/motivo para rótulos legíveis (ex.: 06 →
"Liquidação normal", 09 → "Baixa"). O mapa é declarado por banco/layout, pois os códigos variam.
Estratégia de testes¶
- Fixtures de referência: arquivos de remessa/retorno de referência viram fixtures de teste.
- Comparação byte-a-byte na remessa para os bancos prioritários.
- Round-trip onde aplicável: gerar remessa, reprocessar posições e conferir campos.
- Casos de retorno reais (anonimizados) para validar o mapeamento de ocorrências.