Reconciliação de documentos e eventos CT-e
Os endpoints de documentos (/inbound/documentos) e eventos (/inbound/eventos) operam de forma independente. O client consome cada um em seu próprio loop de…
How-to Guide — Este documento orienta o integrador na construção da lógica de reconciliação entre documentos e eventos CT-e inbound consumidos via API REST.
1. Introdução
Os endpoints de documentos (/inbound/documentos) e eventos (/inbound/eventos) operam de forma independente. O client consome cada um em seu próprio loop de polling, com tokens e filtros separados. No entanto, documentos e eventos estão relacionados: um evento de cancelamento, por exemplo, só faz sentido quando associado ao CT-e que ele cancela.
A reconciliação é a lógica no lado do client que conecta esses dois fluxos. Como a ordem de chegada é imprevisível — um evento pode chegar antes do documento correspondente, e vice-versa — o client precisa lidar com ambos os cenários de forma resiliente.
2. Pré-requisitos
- O client já implementa o loop de sincronização incremental para o endpoint de documentos (ver Fluxo de Listagem de Documentos CT-e inbound).
- O client já implementa o loop de sincronização incremental para o endpoint de eventos (ver Fluxo de Listagem de Eventos CT-e inbound).
- O client possui uma base de dados local onde documentos e eventos são persistidos à medida que são consumidos.
3. Visão geral do modelo
O client mantém dois loops de polling independentes, cada um com seu próprio nextToken e filtros. Ambos os loops gravam os registros recebidos na base local. A reconciliação acontece após cada gravação, usando a chave de acesso como elo de ligação.
┌──────────────────────┐ ┌──────────────────────┐
│ Loop de Documentos │ │ Loop de Eventos │
│ /inbound/documentos │ │ /inbound/eventos │
└────────┬─────────────┘ └────────┬─────────────┘
│ │
▼ ▼
Salva documento Salva evento
na base local na base local
│ │
▼ ▼
Busca eventos pendentes Busca documento
pela chave de acesso pela chave de acesso
│ │
▼ ▼
Encontrou? Encontrou?
├─ Não → fim ├─ Não → fim
└─ Sim → reconcilia └─ Sim → reconcilia
Chave de acesso
A chave de acesso é o identificador que conecta um documento ao(s) seu(s) evento(s):
| Origem | Campo |
|---|---|
Documento (/inbound/documentos) | metadata.chCte (também exposto na raiz do item como chCte) |
Evento (/inbound/eventos) | metadata.chCTe |
Os dois campos têm exatamente o mesmo valor e formato (44 posições, sem prefixo) — diferente da NFS-e, onde o id do documento carrega um prefixo ("NFS...") que não bate literalmente com o infEvento.id do evento (lá, a correlação correta é id do documento vs infPedReg.chNFSe do evento, ambos sem prefixo).
Multiplicidade por empresa: tanto o documento quanto os eventos de um mesmo CT-e podem aparecer mais de uma vez — uma linha por
(companyId, chCte)— quando várias empresas do tenant são partes do transporte (tomador, destinatário, etc.). Reconcilie por(companyId, chCte), não apenas porchCte, para não cruzar o evento de uma empresa com o documento de outra.
O client deve indexar sua base local por (companyId, chCte) para permitir buscas eficientes durante a reconciliação.
4. Ao receber um documento
Quando o loop de documentos retorna um novo item:
- Salvar o documento na base local, independentemente de já existirem eventos associados.
- Buscar na base local todos os eventos cujo
(companyId, chCTe)corresponda ao(companyId, chCte)do documento recém-salvo. - Se nenhum evento for encontrado, encerrar o processamento deste item. Os eventos podem ainda não ter chegado — serão reconciliados quando o loop de eventos os consumir.
- Se um ou mais eventos forem encontrados, associar cada evento ao documento e executar a ação correspondente ao tipo de evento (
tpEvento). Por exemplo, se um evento de cancelamento for encontrado, marcar o documento como cancelado na base local.
5. Ao receber um evento
Quando o loop de eventos retorna um novo item:
- Salvar o evento na base local, independentemente de o documento associado já existir.
- Buscar na base local o documento cujo
(companyId, chCte)corresponda ao(companyId, chCTe)do evento recém-salvo. - Se o documento não for encontrado, encerrar o processamento deste item. O documento pode ainda não ter chegado — será reconciliado quando o loop de documentos o consumir.
- Se o documento for encontrado, associar o evento ao documento e executar a ação correspondente ao tipo de evento. Por exemplo, se for um evento de cancelamento, marcar o documento como cancelado na base local.
6. Considerações
Ordem de chegada imprevisível
Não há garantia de que o documento chegará antes dos seus eventos, nem o contrário. O client deve tratar ambos os cenários com a mesma lógica: salvar primeiro, reconciliar depois.
Idempotência
A lógica de reconciliação deve ser idempotente. Se o mesmo par documento-evento for reconciliado mais de uma vez (por exemplo, após uma reinicialização do client), o resultado final deve ser o mesmo. Isso significa que marcar um documento como cancelado duas vezes não deve gerar efeitos colaterais indesejados.
Múltiplos eventos por documento
Um CT-e pode acumular vários eventos ao longo do tempo (ex.: carta de correção seguida de cancelamento, ou comprovante de entrega seguido de sua própria cancelamento). Use nSeqEvento e tpEvento para ordenar e desduplicar eventos do mesmo tipo, em vez de assumir que o documento só recebe um evento.
Eventos sem documento
É possível que um evento permaneça sem documento associado por um período prolongado — por exemplo, se o documento ainda não foi processado pela API ou se o client iniciou o consumo de eventos com um createdFrom anterior ao dos documentos. O client deve tratar eventos não reconciliados como estado normal, não como erro.
Documentos sem evento
Nem todo documento terá eventos associados. Um CT-e que nunca foi cancelado, corrigido ou sofreu qualquer ação posterior simplesmente não terá eventos. A ausência de eventos para um documento é o cenário padrão.