# RAVAPI · CRM — Contexto do projeto

> Atualizado em 2026-07-30. Descreve a arquitetura, decisões técnicas e estado atual
> do sistema de monitoramento de CNPJs e CRM de prospecção da RAVAPI.

---

## Visão geral

Pipeline Python + portal Next.js para monitorar mensalmente novos CNPJs da Receita Federal
(CNAE alimentação, estados configuráveis), enriquecer com dados de QSA e Simples Nacional,
e gerenciar o ciclo de prospecção via CRM integrado.

---

## Stack

| Camada         | Tecnologia                                         |
|----------------|----------------------------------------------------|
| ETL            | Python 3.12, DuckDB (out-of-memory), psycopg2      |
| Banco          | PostgreSQL 15 @ `26.125.244.194:5432/cnpj_monitor` |
| Portal         | Next.js 16 App Router, React 19, TypeScript        |
| Fontes RFB     | WebDAV SERPRO / mirror Casa dos Dados              |
| Auth           | Cookie httpOnly + sessão no banco (scrypt hash)    |

---

## Banco de dados — tabelas

| Tabela                   | Descrição                                                        |
|--------------------------|------------------------------------------------------------------|
| `empresas_monitoradas`   | CNPJs filtrados pelo ETL (CNAE alimentação + UF configurada)     |
| `socios_administradores` | QSA da RFB — qualificações mapeadas via `QUALIFICACOES_MAP`      |
| `simples_nacional`       | Optantes Simples/MEI por cnpj_basico                             |
| `crm_leads`              | Stage CRM, produto, notas por CNPJ                               |
| `portal_config`          | Configurações key/value do portal (ex: `ufs_monitoradas`)        |
| `usuarios`               | Usuários do portal (role: gestor / vendedor)                     |
| `sessoes`                | Tokens de sessão com expiração (30 dias)                         |
| `crm_tarefas`            | Tarefas de acompanhamento vinculadas a leads e usuários          |

---

## ETL Python (`src/cnpj_monitor/`)

### Arquivos principais

- **`config.py`** — constantes, `QUALIFICACOES_MAP`, `load_ufs_alvo()` (lê UFs do banco)
- **`download_rfb.py`** — download dos ZIPs via WebDAV SERPRO ou mirror Casa dos Dados
- **`etl_load.py`** — processamento DuckDB → upsert PostgreSQL
- **`run_monthly.py`** — orchestrador: download + ETL, invocado pelo portal via subprocess

### Fluxo de dados

```
WebDAV SERPRO → data/extracted/{YYYY-MM}/
  Estabelecimentos (10 partes) → DuckDB filtro UF+CNAE+situação → upsert empresas_monitoradas
  Empresas (10 partes)        → join por cnpj_basico → razao_social, porte, natureza_juridica
  Socios (10 partes)          → DuckDB temp table join → upsert socios_administradores
  Simples (2 arquivos)        → upsert simples_nacional
  Municípios (1 arquivo)      → resolução do código IBGE → nome legível
```

### Filtro configurável de UFs

- `config.py::load_ufs_alvo()` lê `portal_config.ufs_monitoradas` do banco
- Fallback: `UF_ALVO = "MG"` se a tabela não existir ou estiver vazia
- O ETL usa `WHERE uf IN ('MG', 'SP', ...)` em vez de `= 'MG'`

### Qualificações QSA

- `QUALIFICACOES_MAP` em `config.py` mapeia ~40 códigos RFB → descrições
- `etl_load.py::upsert_socios()` usa o mapa para preencher `qualificacao_socio_desc`

### Layout do arquivo Sócios — ATENÇÃO

O arquivo `SociosN.zip` da RFB tem 11 colunas (0-based). A ordem correta é:

| Posição | Campo |
|---------|-------|
| 0 | cnpj_basico |
| 1 | identificador_de_socio (1=PF, 2=PJ, 3=Estrangeiro) |
| 2 | nome_socio_razao_social |
| 3 | cpf_cnpj_socio (para id=3: código do país) |
| 4 | qualificacao_socio |
| 5 | data_entrada_sociedade |
| 6 | pais |
| **7** | **representante_legal (CPF)** |
| 8 | nome_do_representante |
| **9** | **qualificacao_representante_legal** |
| **10** | **faixa_etaria** |

**Bug corrigido (2026-07-30):** posições 7, 9 e 10 estavam trocadas — `faixa_etaria`
estava mapeada como posição 7 (que é o CPF do representante) e `representante_legal`
na posição 9 (que é a qualificação). Isso corrompeu `representante_legal_cpf` e
`qualificacao_representante_desc` para todos os sócios. **Reprocessar o mês corrente
corrige os dados.**

Sócios com `identificador_de_socio = 3` são estrangeiros/residentes no exterior:
- `cpf_cnpj_socio` = código do país (não CPF/CNPJ)
- Qualificação típica: `49` = Sócio-Administrador (residente no exterior)
- O ETL loga quantos foram detectados por rodada

---

## Portal Next.js (`portal/`)

### Estrutura de rotas

```
app/
  login/               ← página de login (sem sidebar)
  (app)/               ← grupo com sidebar + auth
    dashboard/         ← stats + tarefas pendentes
    leads/             ← lista com filtros (UF, município, Simples, origem, estágio)
    leads/[cnpj]/      ← detalhe: dados + QSA + CRM + ContatoFlow drawer
    prospect/          ← leads em estágio avançado
    contatos/          ← sócios deduplicados por CPF/CNPJ
    contatos/[doc]/    ← empresas vinculadas a um contato
    tarefas/           ← lista de tarefas com filtros
    pipeline/          ← pipeline visual de oportunidades
    configuracoes/     ← estados monitorados + pipeline ETL + provedor
    usuarios/          ← gestão de usuários (apenas gestor)
```

### Autenticação

- Middleware (`src/middleware.ts`) redireciona para `/login` se não houver cookie `session`
- Login: POST `/api/auth/login` → verifica hash scrypt → cria sessão no banco → cookie httpOnly 30 dias
- `GET /api/auth/me` → retorna usuário logado (usado pelo `UserContext`)
- Setup inicial: POST `/api/auth/setup` (funciona apenas se não houver usuários)

### Controle de acesso

| Perfil     | O que vê                              |
|------------|---------------------------------------|
| `gestor`   | Todas as tarefas + menu Usuários      |
| `vendedor` | Apenas suas próprias tarefas          |

### ContatoFlow

Drawer lateral (440px) com roteiro de prospecção assistido:

1. **phone-select** — seleciona o número sendo usado (RFB, adicionais ou avulso); opção de informar nome do contato
2. **entry** — tipo de contato (outbound / inbound)
3. **outbound-name-check** — tem o nome do estabelecimento?
4. **msg-with-name / msg-without-name** — template de mensagem com nome real do usuário logado
5. **followup** — régua de follow-up (dias 0, +2, +5)
6. **qualification** — 3 perguntas de qualificação
7. **product-select** — Retail / HCM / TEF / RavSign
8. **meeting** — agendamento de reunião única de demo + proposta

**Comportamento:**
- Cada passo salva automaticamente nas notas do lead via PATCH
- Ao entrar em tela de followup ou reunião, propõe criação de tarefa (com confirmação)
- Nomes com aspas simples da RFB são limpos automaticamente (`cleanNome()`)
- `[SEU NOME]` substituído pelo primeiro nome do usuário logado
- Botão muda para "Continuar contato" quando `stage !== 'novo'`
- Telefones adicionais por lead cadastrados via API `GET/POST/DELETE /api/leads/[cnpj]/telefones`

### Tela de Contatos

- Sócios deduplicados por `cpf_cnpj_socio` (DISTINCT ON)
- Nomes clicáveis → `/contatos/[doc]` (empresas relacionadas)
- Ordenação: Nome A–Z / Mais recente no QSA / Mais antigo no QSA (por `data_entrada_sociedade`)

### Tarefas

- **Criação:** exibe `ConfirmTaskModal` antes de criar (tipo, vencimento, descrição, atribuição)
- **Dashboard:** exibe tarefas vencidas e do dia na página inicial
- **Controle:** gestor vê todas; vendedor vê só as suas
- Tipos: `followup`, `reuniao`, `retorno`, `outro`

### Leads — filtros disponíveis

| Filtro    | Valores possíveis                          |
|-----------|--------------------------------------------|
| Busca     | razão social, CNPJ, município              |
| Estágio   | novo, contato, aguardando, qualificado...  |
| Tipo CRM  | lead / prospect                            |
| UF        | dropdown com todos os 27 estados           |
| Município | texto livre (ILIKE)                        |
| Simples   | Simples Sim / MEI / Sem Simples            |
| Origem    | Receita Federal / Cadastro manual          |

### Cadastro manual de leads

POST `/api/leads` com `source='manual'` — aparece com badge "MANUAL" na lista e detalhe.

---

## Variáveis de ambiente

```env
# portal/.env.local
POSTGRES_DSN=postgresql://postgres:postgres@26.125.244.194:5432/cnpj_monitor

# src/.env
POSTGRES_DSN=postgresql://postgres:postgres@26.125.244.194:5432/cnpj_monitor
```

---

## Migrations

Execute os arquivos abaixo em ordem no banco de produção:

1. `schema.sql` — tabelas base (empresas, socios, simples, crm_leads, portal_config)
2. `migration_auth_tasks.sql` — usuários, sessões, tarefas
3. `migration_telefones.sql` — telefones adicionais por lead (`lead_telefones`)

### Primeiro acesso ao portal

Após rodar as migrations:

```bash
curl -X POST http://localhost:3000/api/auth/setup \
  -H "Content-Type: application/json" \
  -d '{"nome": "Rafael", "email": "seu@email.com", "senha": "suasenha"}'
```

---

## Decisões técnicas importantes

| Decisão | Motivo |
|---|---|
| DuckDB para ETL (não pandas) | Processa ~20GB de CSV sem carregar em RAM |
| Sessão no banco (não JWT) | Simples, sem dependências externas, revogável |
| `scryptSync` nativo do Node.js | Sem precisar instalar `bcrypt` |
| `DISTINCT ON` em subquery no contatos | Permite sort flexível por data sem violar DISTINCT ON |
| Joins compartilhados nas queries de leads | Corrige bug onde filtro Simples falhava no count |
| Fontes das fontes: CSS literal, não `var(--font-*)` no `:root` | `next/font` injeta variáveis no `<body>`, não no `:root` |
| `load_ufs_alvo()` faz conexão tardia | Evita erro de import se psycopg2 não estiver instalado |

---

## Produtos RAVAPI (usados no CRM)

| Código  | Produto         | Descrição                                           |
|---------|-----------------|-----------------------------------------------------|
| retail  | Retail          | PDV, retaguarda, totem de autoatendimento           |
| hcm     | HCM             | Ponto eletrônico, escala, folha de pagamento        |
| tef     | TEF             | Transação de cartão integrada ao PDV (via CTF)      |
| ravsign | RavSign         | Certificado digital (empresa do grupo RAVAPI)       |

---

## Estágios CRM

| Stage          | Tipo      | Descrição                          |
|----------------|-----------|------------------------------------|
| novo           | lead      | Detectado, sem contato             |
| contato        | lead      | Primeiro contato iniciado          |
| aguardando     | lead      | Aguardando resposta                |
| qualificado    | prospect  | Qualificado pelas 3 perguntas      |
| produto        | prospect  | Produto de interesse identificado  |
| reuniao        | prospect  | Reunião de demo + proposta agendada|
| ganho          | prospect  | Fechado com sucesso                |
| desqualificado | —         | Sem interesse / sem resposta       |
