Skip to content

CNPJ Alfanumérico — Plano de Adaptação

Status: ✅ Implementado (2026-03-12)
Prazo externo: 1º de julho de 2026 (novos CNPJs alfanuméricos entram em vigor)
Impacto: 16 pontos de alteração em 10 arquivos (4 backend, 6 frontend)


Contexto

A partir de julho/2026, a Receita Federal emitirá CNPJs com caracteres alfanuméricos nos 12 primeiros dígitos (base), mantendo os 2 últimos como dígitos verificadores numéricos. CNPJs existentes permanecem 100% numéricos.

Formato: AA.AAA.AAA/AAAA-DD (A = alfanumérico, D = dígito verificador)


Algoritmo de Validação (Módulo 11 — SERPRO)

O algoritmo é unificado — a mesma fórmula funciona para CNPJs numéricos e alfanuméricos.

Conversão de caractere para valor

valor = charCodeAt(caractere) - 48
CaractereASCIIValor
0480
9579
A6517
Z9042

Cálculo dos Dígitos Verificadores

DV1 — pesos [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] sobre posições 0-11:

soma = Σ (valor[i] × peso[i]) para i = 0..11
resto = soma % 11
DV1 = (resto < 2) ? 0 : 11 - resto

DV2 — pesos [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] sobre posições 0-12:

soma = Σ (valor[i] × peso[i]) para i = 0..12
resto = soma % 11
DV2 = (resto < 2) ? 0 : 11 - resto

Implementação

typescript
const getVal = (ch: string) => ch.charCodeAt(0) - 48;

Funciona identicamente para ambos os formatos:

  • getVal('3')51 - 48 = 3
  • getVal('A')65 - 48 = 17

Estado atual do sistema

Compatível (sem alteração necessária)

LocalPor quê
escolas.cnpj (TEXT, nullable)Campo TEXT, constraint valida apenas length = 14
usuarios.codigo (TEXT) + tipo_codigo = 'cnpj'Campo TEXT, sem restrição de conteúdo
UNIQUE e INDEX sobre cnpjFuncionam com texto alfanumérico
Lookups no banco (escolas.cnpj = valor)Normalização preserva letras
Exibição read-only (inscricoes.tsx, pagamentos-escola.tsx, etc.)Exibem valor armazenado

Alterações implementadas

Frontend — Helpers (src/lib/masks.ts)

FunçãoAlteração
unmaskCNPJ()Nova — remove apenas .-/, preserva letras
maskCNPJ()Aceita alfanumérico ([^a-zA-Z0-9] em vez de \D)
isValidCNPJFormat()Usa unmaskCNPJ em vez de unmask
isValidCNPJ()Algoritmo unificado charCodeAt - 48
unmask()Sem alteração — continua apenas para CPF/CEP/telefone/INEP

Frontend — Auth (src/lib/auth.ts)

FunçãoAlteração
normalizarCodigo()Condicional: se contém letras, preserva; senão, remove não-dígitos
identificarTipoCodigo()Detecta CNPJ alfanumérico (14 chars com letras)
formatarCNPJ()Usa slice posicional em vez de regex numérico

Frontend — PDF (src/lib/pdf-helpers.ts)

LocalAlteração
L48, L151Substituiu regex inline por formatarCNPJ() importado

Frontend — Componentes

ArquivoAlteração
login-unified.tsxaplicarMascaraCodigo detecta letras → maskCNPJ alfanumérico
CadastroTrialPage.tsxunmask(cnpj)unmaskCNPJ(cnpj)
CadastroFormulario.tsxunmask(cnpj)unmaskCNPJ(cnpj)
admin-escolas.tsxunmask(formData.cnpj)unmaskCNPJ(formData.cnpj)

Backend — Shared

ArquivoDescrição
_shared/cnpj-validator.tsNovonormalizarCNPJ, isValidCNPJ, normalizarCodigo, identificarTipoCodigo

Backend — Edge Functions

ArquivoAlteração
send-otp/index.tsNormalização condicional + mensagem "14 caracteres"
verify-otp/index.tsNormalização condicional
cadastro-escola-publica/index.tsreplace(/\D/g, "")replace(/[.\-\/]/g, "").toUpperCase() (2 locais)

Testes unitários

Arquivo: src/lib/__tests__/cnpj-alfanumerico.test.ts

  • unmaskCNPJ preserva letras
  • maskCNPJ formata alfanumérico corretamente
  • isValidCNPJ valida CNPJ numérico existente (regressão)
  • isValidCNPJ rejeita DVs incorretos em alfanumérico
  • identificarTipoCodigo detecta CNPJ alfanumérico
  • normalizarCodigo preserva letras quando presentes
  • formatarCNPJ formata alfanumérico

Compatibilidade retroativa

✅ Total — CNPJs numéricos existentes continuam funcionando sem alteração. O algoritmo charCodeAt - 48 produz resultados idênticos ao parseInt para caracteres 0-9.


Referências