Programação Por Equipe DevBras

Capa do artigo: NF-e no seu sistema: os estados da nota antes e depois do XML

NF-e no seu sistema: os estados da nota antes e depois do XML

Modele a nota fiscal eletrônica como uma máquina de estados: numeração sem buracos, assinatura, autorização, rejeição, consulta após timeout e o que guardar quando a Sefaz autoriza.

Sexta, 17h50, a Ferragens Boa Vista (fictícia) fecha uma venda grande. O sistema monta o XML, envia, a conexão cai no meio e a tela mostra "erro". O vendedor clica de novo. Agora existem duas tentativas para a mesma venda, e ninguém sabe se alguma foi autorizada. A resposta curta: a NF-e não é um arquivo, é um pedido de autorização com estados. Ela só existe quando a Sefaz devolve o protocolo de autorização. Modele no banco cada etapa (numerada, assinada, enviada, autorizada, rejeitada, denegada, cancelada) e, quando não souber o resultado, consulte antes de reenviar.

Este guia trata do fluxo dentro do seu sistema: o que gravar, em que ordem e como reagir a cada resposta. Regra tributária, CFOP e alíquota ficam com o contador. Isto não substitui a orientação de um profissional de contabilidade nem o manual oficial da NF-e.

Por que "gerar o XML" não é emitir a nota

O XML montado no seu servidor é um rascunho. Ele precisa ser assinado com o certificado digital da empresa, enviado ao web service da Sefaz do estado e validado lá. A resposta pode ser autorização, rejeição ou denegação. Só com autorização a nota vale, e o arquivo que você guarda e entrega ao cliente é o XML assinado junto com o protocolo, chamado de nfeProc.

Cada resultado tem consequência diferente para o número da nota:

  • Autorizada (código de status 100): o número está usado. A nota existe e só sai de cena por evento de cancelamento, dentro do prazo que a legislação permite.
  • Rejeitada: a Sefaz recusou por erro de preenchimento ou de esquema. A nota não foi registrada, e o mesmo número pode ser reenviado depois da correção.
  • Denegada: o uso foi negado por irregularidade cadastral do emitente ou do destinatário. O número fica consumido e não pode ser reaproveitado.

Existe ainda o estado que mais causa problema: sem resposta. Timeout, queda de rede, Sefaz lenta. O pedido pode ter chegado e sido autorizado, e você não sabe. Reenviar no escuro é o caminho para a rejeição por duplicidade ou, pior, para gastar outro número numa venda que já tinha nota.

A chave de acesso e o número que não pode pular

Toda NF-e tem uma chave de acesso de 44 dígitos, montada a partir de dados da própria nota:

  • código da UF (2 dígitos) e ano e mês de emissão (4);
  • CNPJ do emitente (14) e modelo do documento (2; NF-e é 55);
  • série (3) e número da nota (9);
  • forma de emissão (1), código numérico aleatório (8) e dígito verificador (1).

O dígito verificador usa módulo 11 com pesos de 2 a 9 aplicados da direita para a esquerda. Se o resto da divisão por 11 for 0 ou 1, o dígito é 0; caso contrário, é 11 menos o resto. Em PHP:

function digitoChave(string $chave43): int
{
    $soma = 0;
    $peso = 2;
    for ($i = strlen($chave43) - 1; $i >= 0; $i--) {
        $soma += (int) $chave43[$i] * $peso;
        $peso = $peso === 9 ? 2 : $peso + 1;
    }
    $resto = $soma % 11;
    return $resto < 2 ? 0 : 11 - $resto;
}

Bibliotecas fiscais já fazem essa conta. Vale conhecê-la para escrever um teste e para reconhecer uma chave digitada errado no suporte.

O número da nota é sequencial por série. Se o sistema reservar o 1533 e a venda for abandonada antes do envio, o 1533 vira um buraco, e buraco na numeração precisa ser inutilizado junto à Sefaz por um pedido próprio. Por isso a reserva do número deve ser atômica e registrada. Em MySQL, uma tabela de sequência resolve sem corrida entre dois caixas:

CREATE TABLE nfe_sequencia (
  ambiente TINYINT NOT NULL,
  serie SMALLINT NOT NULL,
  ultimo_numero INT NOT NULL,
  PRIMARY KEY (ambiente, serie)
);

UPDATE nfe_sequencia
   SET ultimo_numero = LAST_INSERT_ID(ultimo_numero + 1)
 WHERE ambiente = 1 AND serie = 1;

SELECT LAST_INSERT_ID();

O LAST_INSERT_ID(expr) guarda o novo valor na conexão atual, e o UPDATE trava a linha durante a transação. Duas vendas simultâneas nunca recebem o mesmo número.

Como modelar a nota no banco, passo a passo

  1. Crie a linha da nota antes de qualquer chamada externa, com pedido, ambiente, série, número e status numerada.
  2. Monte o XML num módulo isolado, que recebe a venda e devolve o documento. A tela de pedido não deve conhecer tag de imposto. Use uma biblioteca de montagem, não concatenação de texto; o guia gerar XML sem concatenar mostra por quê.
  3. Assine e grave o XML assinado, com status assinada e a chave de 44 dígitos.
  4. Marque enviada antes de transmitir, com data e hora. Se o processo morrer aqui, você sabe que precisa consultar.
  5. Trate a resposta. Autorizada: grave protocolo e nfeProc. Rejeitada: grave código e motivo, volte para correção. Denegada: grave e bloqueie o número.
  6. Sem resposta: consulte pela chave. O serviço de consulta de protocolo diz se aquela chave foi autorizada. Só reenvie se a Sefaz não conhecer a nota.
  7. Cancelamento é evento, não DELETE. Grave o evento e o protocolo do cancelamento junto da nota original.
CREATE TABLE nfe (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  pedido_id BIGINT NOT NULL,
  ambiente TINYINT NOT NULL,
  serie SMALLINT NOT NULL,
  numero INT NOT NULL,
  chave CHAR(44) NULL,
  status VARCHAR(20) NOT NULL,
  cstat SMALLINT NULL,
  motivo VARCHAR(255) NULL,
  protocolo VARCHAR(20) NULL,
  xml_assinado MEDIUMTEXT NULL,
  xml_autorizado MEDIUMTEXT NULL,
  enviada_em DATETIME NULL,
  atualizada_em DATETIME NOT NULL,
  UNIQUE KEY uk_numero (ambiente, serie, numero),
  UNIQUE KEY uk_chave (chave)
);

A coluna ambiente segue o campo tpAmb da NF-e (1 para produção, 2 para homologação) e entra na chave única. Isso impede que um número de teste ocupe o lugar de uma nota real. Misturar os dois ambientes é um clássico, tratado em homologação não é produção.

O que o usuário vê quando a Sefaz rejeita

O retorno traz um código numérico e um texto técnico. Mostre ao usuário o motivo traduzido para a ação ("CPF do cliente inválido: corrija no cadastro e reenvie") e guarde o código cru no log. Quem atende o caixa às 18h não deve precisar abrir o manual para saber o que fazer.

Erros comuns

  • Reenviar após timeout sem consultar. Gera rejeição por duplicidade ou duas notas para a mesma venda.
  • Reservar número fora de transação. Dois caixas pegam o mesmo número e um deles é rejeitado em produção.
  • Deixar buraco na numeração sem inutilizar. A sequência fica inconsistente e vira pendência fiscal para resolver depois.
  • Guardar só o XML enviado. Sem o protocolo, você não consegue provar a autorização nem entregar o arquivo correto ao cliente.
  • Imposto fixo no código. A regra muda, o cliente muda de regime, e a nota sai errada até alguém lembrar onde estava o número mágico.
  • Certificado vencendo sem alerta. No dia seguinte ao vencimento, nenhuma nota é assinada e a loja para.

Checklist

  • Cada nota tem uma linha no banco desde a reserva do número.
  • A reserva do número é atômica e separada por ambiente e série.
  • Os status cobrem numerada, assinada, enviada, autorizada, rejeitada, denegada e cancelada.
  • Uma nota em enviada sem resposta é consultada pela chave antes de qualquer reenvio.
  • O nfeProc autorizado fica guardado e é a única fonte para reenvio ao cliente.
  • Existe alerta de vencimento do certificado digital com semanas de antecedência.
  • Números abandonados entram numa rotina de inutilização.
  • Mensagens de rejeição aparecem traduzidas para a ação que o usuário precisa tomar.

Procure no seu banco uma nota que ficou em "enviada" ou "erro" sem protocolo. Se a Sefaz caísse agora no meio de uma transmissão, o seu sistema saberia consultar aquela chave, ou o próximo clique do vendedor gastaria um número novo?

Avalie este artigo

Referência: http://www.linhadecodigo.com.br/artigo/1695/iniciando-um-projeto-de-nota-fiscal-eletronica-nfe.aspx