Pular para o conteúdo principal

Síncrono, assíncrono e polling

Este guia trata de duas coisas diferentes que costumam aparecer juntas: o cliente assíncrono do Python (AsyncNfeClient, com async/await) e a emissão assíncrona da API, em que a nota leva alguns segundos para ser autorizada pela prefeitura.

A emissão é assíncrona​

service_invoices.create responde quase sempre com 202 Accepted. A nota volta com id e com um flow_status intermediário (WaitingCalculateTaxes ou WaitingSend, por exemplo) e segue sendo processada até um estado final, em geral em poucos segundos.

Os estados conhecidos estão em nfeio.types.FlowStatus:

GrupoValores
SucessoIssued, Cancelled (FlowStatus.SUCCESS)
FalhaIssueFailed, CancelFailed, Error (FlowStatus.FAILURE)
Em andamentoWaitingCalculateTaxes, WaitingDefineRpsNumber, WaitingSend, WaitingReturn, WaitingSendCancel, WaitingReturnCancel, WaitingDownload, PullFromCityHall

O conjunto é aberto: compare strings. Um valor que o SDK não conhece é tratado como "ainda em andamento".

Três formas de saber o resultado​

FormaQuando usar
create_and_waitScripts, jobs e filas: envia e espera no mesmo passo.
create + waitQuando você precisa gravar o id antes de esperar.
create + webhookAplicações web: responda ao usuário na hora e receba o resultado por push.

create_and_wait​

from decimal import Decimal

from nfeio import InvoiceProcessingError, NfeClient, PollingTimeoutError

client = NfeClient()
company_id = "ID_DA_EMPRESA"

try:
invoice = client.service_invoices.create_and_wait(
company_id,
{
"cityServiceCode": "10677",
"description": "Consultoria em tecnologia",
"servicesAmount": Decimal("150.00"),
},
external_id="pedido-1002",
timeout=180, # segundos de espera; o padrão é 120
)
print(invoice.flow_status) # "Issued"
except InvoiceProcessingError as erro:
# A prefeitura recusou: IssueFailed ou Error. flow_message explica o motivo.
print(erro.flow_status, erro.flow_message)
except PollingTimeoutError as erro:
# O prazo acabou antes de um estado final. A nota existe e continua sendo processada.
print("ainda processando:", erro.flow_status, erro.invoice.id if erro.invoice else None)

PollingTimeoutError não significa que a emissão falhou: a nota foi aceita e continua na fila da prefeitura. Guarde o id e consulte depois com retrieve ou wait. Nunca emita de novo por causa de um timeout de espera.

create + wait​

from decimal import Decimal

from nfeio import NfeClient

client = NfeClient()
company_id = "ID_DA_EMPRESA"

accepted = client.service_invoices.create(
company_id,
{"cityServiceCode": "10677", "description": "Suporte", "servicesAmount": Decimal("80.00")},
external_id="pedido-1003",
)
assert accepted.id is not None
print(accepted.id, accepted.flow_status) # id já disponível, ex.: WaitingCalculateTaxes

invoice = client.service_invoices.wait(company_id, accepted.id)
print(invoice.flow_status)

Parâmetros de espera​

wait, create_and_wait e cancel_and_wait aceitam os mesmos parâmetros:

ParâmetroPadrãoO que faz
timeout120.0Tempo máximo de espera, em segundos.
initial_interval1.0Intervalo da primeira consulta.
max_interval10.0Teto do intervalo entre consultas.
backoff1.5Multiplicador do intervalo a cada consulta (entre 1 e 10).
not_found_grace10.0Por quanto tempo um 404 logo após o 202 é tolerado.
raise_on_failureTrueCom False, um estado de falha é devolvido em vez de levantar InvoiceProcessingError.

O polling usa GET da própria nota (a API não tem rota de status), com backoff até max_interval.

Cancelamento​

O cancelamento também é assíncrono. cancel_and_wait pede o cancelamento e espera Cancelled:

from nfeio import NfeClient

client = NfeClient()

invoice = client.service_invoices.cancel_and_wait("ID_DA_EMPRESA", "ID_DA_NOTA")
assert invoice.flow_status == "Cancelled"

Se a prefeitura recusar, sobe InvoiceProcessingError com flow_status == "CancelFailed".

Cliente assíncrono​

AsyncNfeClient tem os mesmos recursos, métodos, parâmetros e comportamento do NfeClient. A diferença é que cada método é uma corrotina, e a paginação usa async for.

import asyncio
from decimal import Decimal

from nfeio import AsyncNfeClient


async def main() -> None:
async with AsyncNfeClient() as client:
invoice = await client.service_invoices.create_and_wait(
"ID_DA_EMPRESA",
{
"cityServiceCode": "10677",
"description": "Consultoria em tecnologia",
"servicesAmount": Decimal("150.00"),
},
external_id="pedido-1004",
)
print(invoice.flow_status)

page = await client.companies.list()
async for company in page.auto_paging_iter():
print(company.id, company["name"])


asyncio.run(main())

Para emitir várias notas em paralelo, use asyncio.gather com um external_id por nota e return_exceptions=True, para que a falha de uma nota não esconda o resultado das outras:

import asyncio
from decimal import Decimal

from nfeio import AsyncNfeClient, ServiceInvoice


async def emitir_lote(
pedidos: list[tuple[str, Decimal]],
) -> list[ServiceInvoice | BaseException]:
async with AsyncNfeClient() as client:
tarefas = [
client.service_invoices.create_and_wait(
"ID_DA_EMPRESA",
{"cityServiceCode": "10677", "description": "Assinatura", "servicesAmount": valor},
external_id=f"pedido-{pedido_id}",
)
for pedido_id, valor in pedidos
]
return await asyncio.gather(*tarefas, return_exceptions=True)


resultados = asyncio.run(emitir_lote([("2001", Decimal("49.90")), ("2002", Decimal("99.90"))]))
for resultado in resultados:
if isinstance(resultado, BaseException):
print("falhou:", repr(resultado)) # trate como em "Tratamento de erros"
else:
print(resultado.id, resultado.flow_status)

Como funciona por dentro: o transporte padrão roda a E/S em threads (asyncio.to_thread) e as esperas usam asyncio.sleep, então o event loop nunca é bloqueado. Para uma pilha HTTP nativamente assíncrona, implemente nfeio.transport.AsyncTransport.

Cancelar uma task não cancela a requisição

Cancelar a task não interrompe uma requisição que já está em voo: ela termina na thread. Se a task cancelada estava emitindo uma nota, trate como resultado incerto e reconcilie por external_id antes de tentar de novo (veja Tratamento de erros).

Veja também​

NFE.io

A NFE.io é uma empresa de tecnologia que fornece soluções para automatizar e simplificar a emissão e gestão de notas fiscais eletrônicas. Com suas ferramentas, as empresas podem economizar tempo e reduzir erros, aumentando a eficiência e precisão do processo de emissão de notas fiscais.

Um dos principais cases de sucesso da NFE.io é a implementação da solução na empresa de transporte Rodonaves. Com a automatização da emissão e gestão de notas fiscais eletrônicas, a Rodonaves conseguiu reduzir em até 80% o tempo gasto nesse processo, o que se traduziu em uma significativa melhoria na eficiência operacional. Além disso, a empresa também conseguiu eliminar erros e atrasos na emissão de notas fiscais, o que melhorou a relação com seus clientes e aumentou a confiança dos órgãos fiscais.

Outro exemplo é a implementação da NFE.io na empresa de comércio eletrônico, a Loja Integrada. Com a automatização da emissão de notas fiscais, a Loja Integrada conseguiu aumentar a velocidade de emissão de notas em até 10 vezes, o que permitiu que a empresa atendesse a uma maior quantidade de clientes e, consequentemente, aumentar as suas vendas.

Além desses exemplos, a NFE.io também tem outros cases de sucesso com empresas de setores como indústria, construção, varejo e serviços, mostrando a versatilidade e eficácia da sua solução.

Em resumo, a NFE.io é uma empresa de tecnologia que oferece soluções para automatizar e simplificar a emissão e gestão de notas fiscais eletrônicas, ajudando as empresas a economizar tempo e reduzir erros, melhorando a eficiência e precisão do processo. Com cases de sucesso em diferentes setores, a NFE.io tem se destacado como uma empresa líder em automação fiscal.