feat(client-registrations): busca automática de CNPJ (dados, sócios, IE)

Ao digitar/já ter um CNPJ num cadastro de cliente PJ, busca automática
contra a Receita Federal (cnpj.ws primário, BrasilAPI fallback) e
preenche razão social, endereço e inscrição estadual, além de criar os
sócios encontrados (sem duplicar em buscas repetidas) — pra usar depois
na confecção do contrato/assinatura.

Fecha uma lacuna real que já estava prevista mas nunca implementada:
pj_cnpj não tinha nenhuma forma de ser gravado (UpdateClientRegistrationDto
o excluía deliberadamente, esperando por "a dedicated re-verification
flow" — exatamente o que este endpoint é). O servidor sempre refaz a
própria consulta autoritativa a partir do CNPJ recebido, nunca confia
em dado "já encontrado" vindo do cliente.

Suíte: 100/100 (95→100). pnpm turbo run typecheck build: 28/28.
Testado ao vivo contra CNPJs reais (Petrobras, Banco do Brasil) e em
navegador real via Playwright.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 13:12:15 -03:00
parent 53c3031775
commit 93cd42b22e
14 changed files with 783 additions and 20 deletions

View File

@@ -694,3 +694,29 @@ Isso exercita profundidade 3 no `AppShell` (Cadastro > Produtos > Grupos > Subgr
### Pendências
Ajustes na tela de Cadastro de Cliente — usuário sinalizou que é o próximo passo, ainda sem especificar quais ajustes.
---
## Busca automática de CNPJ (dados da empresa, sócios, inscrição estadual) no Cadastro de Cliente
Data: 2026-09-18. O ajuste pedido na leva anterior era este: usuário apontou `/home/handix/cadastro.md` (fora do repo), documentando como o legado OrçaFácil consulta CNPJ pra pré-preencher razão social/endereço/inscrição estadual e a lista de sócios (pra usar depois na confecção do contrato/assinatura), via duas APIs públicas gratuitas (cnpj.ws principal — única com inscrição estadual —, BrasilAPI como fallback).
**Achado real antes de implementar**: `pj_cnpj` (e `pf_cpf`/`pj_rep_cpf`) **não tinham nenhuma forma de ser gravados** — `UpdateClientRegistrationDto` os exclui deliberadamente, com um comentário no próprio código: "Changing the document itself would need a dedicated re-verification flow — never a side effect of a generic admin PATCH." Era exatamente essa lacuna, já prevista mas nunca fechada, que esta feature resolve.
### Backend (agente `eden-commercial`)
`packages/integrations/src/cnpj-lookup/lookup.ts` (novo adapter, `fetch` nativo do Node 22, sem dependência nova): `lookupCnpj(cnpj)` — cnpj.ws primário, BrasilAPI fallback só em erro de rede/5xx (404 explícito do primário = "não existe", não cai pro fallback), timeout de 10s. Endpoint novo `POST /v1/client-registrations/:id/cnpj-lookup` — uma ação só, dedicada (sem endpoint de "preview" separado, mais fiel ao "ao perder o foco já preenche" do legado): o servidor SEMPRE refaz a própria consulta autoritativa a partir do CNPJ recebido (nunca confia num payload do cliente dizendo o que "já encontrou"), grava `pj_cnpj` + todos os campos PJ + endereço via uma função de repositório nova e dedicada (`applyCnpjLookup`, o único lugar que pode tocar `pj_cnpj` — `COALESCE` em tudo mais pra nunca apagar dado já preenchido com um campo vazio da API), cria sócios novos com dedup por `document` (rodar de novo não duplica), devolve `{registration, partners}`. 404 se o CNPJ não for encontrado em nenhuma fonte (nenhum efeito colateral). 400 se o cadastro for PF.
**Decisão registrada**: sócios vindos do lookup usam `source: 'cnpj_lookup'`, não o `'brasilapi'` hardcoded do legado — já que cnpj.ws é a fonte primária real nesta porta, o valor do legado seria uma atribuição enganosa.
Verificado: `pnpm turbo run typecheck build` 28/28, suíte 100/100 (95→100, 5 testes novos com `lookupCnpj` mockado, sem dependência de rede). Testado ao vivo contra internet real: Petrobras (`33000167000101`, 10 sócios) e Banco do Brasil (`00000000000191`, 96 sócios) — dados corretos, inscrição estadual resolvida corretamente (ativa por UF, fallback pra primeira do array se nenhuma ativa); reexecução no mesmo cadastro não duplicou sócios; CNPJ inexistente devolveu 404 sem alterar nada. Achado ao vivo (não no plano original): cnpj.ws também devolve 429 (rate limit da própria API pública) além dos 5xx documentados — tratado igual, cai pro fallback normalmente.
### Frontend (agente `eden-frontend`)
`ClientRegistrationDetailPage.tsx`: campo CNPJ (dentro de "Dados PJ") agora tem dois estados — sem `pj_cnpj` ainda: `Input` editável + botão "Buscar CNPJ" (desabilitado até 14 dígitos); já com `pj_cnpj`: valor formatado somente-leitura + botão "Buscar novamente" (permite atualizar situação cadastral/endereço/IE que podem mudar com o tempo, sem redigitar o CNPJ). Sucesso mostra quantos sócios ficaram no cadastro; 404 mostra a mensagem exata da API num `Alert` de aviso; a lista de Sócios (`PartnersSection.tsx`, componente independente) ganhou um prop `refreshKey` pra recarregar depois de um lookup bem-sucedido, sem precisar de F5.
### Verificação
`pnpm turbo run typecheck build` 28/28. Suíte 100/100. Testado ao vivo em navegador real via Playwright: cadastro PJ novo → CNPJ da Petrobras → título da página, endereço, todos os campos PJ e os 10 sócios preenchidos automaticamente na tela, confirmado por screenshot, sem reload manual. Cadastro de teste desativado ao final.
### Pendências
Lookup de CPF pra pessoa física — fora de escopo, não foi pedido. Refresh automático da situação cadastral/endereço (ex. um job periódico) não existe — "Buscar novamente" é manual, sob demanda.