
Sistema de gestão comercial
| Área | Gestão e integração |
|---|---|
| O que faz | Lead chegava repetido e sem dono. Agora o CNPJ identifica a conta e a região define o vendedor. |
| Funções | Lead sem duplicidade · Vendedor por região · Previsão de vendas |
| Habilidades | Mapeamento do funil · Design de telas · Regras de distribuição · Integração com WhatsApp |
O que o sistema cobre.
Sistema para a área comercial de empresas que vendem para outras empresas. Cobre do lead ao fechamento: captação por formulário, planilha, e-mail e WhatsApp, cadastro único por CNPJ, distribuição por território, funil com motivo de perda, proposta com alçada de desconto, régua de contato automática e previsão de vendas que compara a promessa do vendedor com o histórico.
Para quem
Diretor comercial, gerentes de vendas e vendedores de empresas que vendem B2B, com carteira dividida por região e equipe.
O que mudou na empresa
A escolha do vendedor, a checagem de cadastro repetido e o envio da régua de contato deixaram de ser manuais. Perda sem motivo, desconto fora da alçada e negócio parado passaram a ser controlados.
Módulos e funções.
8 partes do sistema, cada uma com o que faz.
Captação de leads
Recebe lead de formulário, planilha, e-mail, WhatsApp e marketing, com origem, campanha e base legal.
Cadastro único por CNPJ
CNPJ identifica a conta, matriz e filial separadas; cadastro repetido é unido sem perder histórico.
Distribuição por território
Estado, ramo e faturamento definem o território; o rodízio escolhe o vendedor, respeitando carteira e ausência.
Funil de vendas
Cada etapa tem critério de saída, perda exige motivo e cada mudança registra autor e data.
Propostas com alçada
Proposta enviada fica congelada com preço da tabela vigente; desconto acima da alçada espera aprovação.
Régua de contato
Envia e-mail e WhatsApp em dias programados, no horário comercial; resposta do lead interrompe a sequência.
Hierarquia e permissões
Cada gestor vê só a própria equipe; troca de cargo vale na hora, com auditoria.
Previsão de vendas
Foto diária do funil; a previsão mostra a promessa do vendedor ao lado da taxa histórica.
Três telas.
O design é meu. Cada legenda diz o que a tela faz.



Como funciona por dentro.
Cada lead entra uma vez. Cada gestor vê exatamente o seu pedaço. O forecast não é chute somado.
- CaptaFormulário, importação de planilha, API, automação de marketing, e-mail e WhatsApp. Grava o payload cru com origem, campanha e carimbo de tempo antes de interpretar qualquer campo. A base legal da LGPD entra junto com o registro, não depois, é ela que define o prazo de retenção e o caminho de eliminação lá na frente.
- NormalizaCNPJ separado em raiz e filial, telefone em E.164, e-mail em minúsculas sem apelido de rótulo, razão social sem tipo societário nem acento. O valor original fica guardado ao lado do normalizado, a normalização serve para casar, não para reescrever o que o cliente digitou.
- Resolve identidadeChave de casamento, não de unicidade. Único de verdade só em (organização, CNPJ de 14 dígitos) na conta viva; a raiz é índice de busca e agrupa as filiais sob um grupo econômico, porque matriz e filial são contas distintas. E-mail e telefone são índice não único, com bloqueio de domínio e central genéricos: contato@empresa e o telefone da recepção não fundem ninguém. O que sobra vai para trigram na razão social mais domínio, score alto funde, faixa do meio abre tarefa de revisão, score baixo cria registro novo. O alias do perdedor mora em tabela separada, apontando para o id vencedor, e por isso não disputa o índice único.
- RoteiaTerritório casa UF, CNAE e faixa de faturamento. Dentro do território, o rodízio pega o próximo da fila com SELECT … FOR UPDATE SKIP LOCKED e contador por fila, respeitando teto de carteira e ausência declarada. Sem lock, duas entradas simultâneas caem no mesmo vendedor.
- Move no funilCada etapa tem critério de saída obrigatório e a perda exige motivo cadastrado naquele funil, não numa lista global. Mudança de etapa grava evento com autor, data e valor anterior; o campo atual é projeção do histórico, nunca o contrário.
- PropõeProposta é revisão imutável: itens, preço, desconto e condição congelados no momento do envio, com o preço vindo da tabela vigente. Revisão nova nasce da anterior e zera a aprovação. Desconto acima da alçada trava até o aval de quem tem a faixa.
- Executa cadênciaA régua gera atividade em D+0, D+1, D+3 e D+7 na mesma transação que moveu a etapa. E-mail e WhatsApp saem por outbox com estado explícito: pendente, enviado, confirmado. O envio é at-least-once, nenhum desses canais aceita chave de idempotência do cliente. O retry só dispara depois que o webhook de status ou o message id devolvido confirmam que a primeira não saiu, e mensagem igual para o mesmo destino é suprimida dentro da janela. Quando não dá para confirmar, a régua para e vira tarefa humana. Fora das 24 horas do WhatsApp, só template aprovado.
- Fotografa e calculaTodo dia o pipeline inteiro é congelado numa tabela particionada por competência, daí sai o que entrou, saiu, moveu e escorregou. O forecast mostra a categoria declarada pelo vendedor e, ao lado, a taxa histórica da célula etapa × segmento × faixa de ticket × idade, com mínimo de oportunidades encerradas por célula e recuo para o nível acima quando o mínimo não bate.
O problema e a saída.
O problema
O mesmo cliente entra por formulário, planilha e WhatsApp e vira três registros com três donos. O funil acumula negociação parada sem motivo declarado. E o forecast é a soma de probabilidades que o vendedor escolheu no mês em que precisa parecer bem.
A saída
Identidade na entrada: CNPJ completo casa a conta, a raiz agrupa filiais, e-mail e telefone só apontam candidatos. O merge guarda o perdedor como alias. Visibilidade sai de closure table versionada. O forecast põe o commit ao lado da taxa histórica.
Tecnologias e o porquê.
| Camada | Escolha | Por quê |
|---|---|---|
| Dados | PostgreSQL; conta e contato com id sobrevivente no merge | Merge de duplicata não apaga linha: o id perdedor vira apelido do sobrevivente e continua resolvendo. Assim atividade, proposta e e-mail antigos seguem apontando para algo válido, e desfazer um merge errado é reverter um ponteiro, não restaurar backup. |
| Identidade e dedupe | pg_trgm + GIN, depois das chaves determinísticas | Único de verdade só no CNPJ de 14 dígitos por organização. Raiz, e-mail e telefone são índice de busca, não de unicidade: matriz e filial são contas distintas, e meia carteira B2B compartilha o mesmo contato@ e a mesma central. O trigram roda só no que sobra, sobre um bloco de candidatos, e nunca funde sozinho na faixa de dúvida, vira fila humana. Merge automático errado custa mais caro que duplicata. |
| Permissão | Closure table de cargos, territórios e equipes | Hierarquia comercial muda a cada reorganização, e consulta recursiva a cada leitura de pipeline não paga. A RLS lê a árvore materializada por versão, o join está dentro da política, não na aplicação. Mudança de cargo invalida a versão na hora; enquanto o recálculo em background não termina, a leitura cai no caminho recursivo, mais lento e correto, em vez de servir a árvore velha e mostrar pipeline de ex-subordinado. A auditoria guarda quem enxergava o quê na data, para relatório antigo não mudar de valor. |
| Fila e cadência | Agendador por fuso do lead, com janela e supressão | A cadência não dispara por relógio do servidor: cada passo é agendado no fuso do lead, respeita horário comercial e feriado, e cai fora da janela de 24h do WhatsApp volta para template aprovado. Resposta do lead suprime o resto da sequência na hora, e bounce forte tira o endereço de circulação em vez de queimar reputação de IP. |
| Canais | WhatsApp Cloud API e IMAP/SMTP atrás de outbox | Nenhum envio acontece dentro de transação. O worker grava a intenção, solta a conexão, envia e concilia pelo webhook de status e pelo message id devolvido. Nem o Graph do WhatsApp nem o SMTP aceitam chave de idempotência do cliente, então retry cego depois de timeout vira segunda entrega no celular do cliente. O que dá para fazer é confirmar o estado antes de reenviar, suprimir mensagem igual dentro da janela e parar a régua quando a confirmação não vem, at-least-once com dedup best-effort, assumido. |
| Forecast | Taxa histórica versionada, em job Python fora da leitura | A taxa sai de janela móvel de oportunidades encerradas, com mínimo por célula: se etapa × segmento × faixa de ticket × idade não junta o mínimo, recua para etapa × segmento e depois para etapa. Negociação ainda aberta não conta como perda, entra como censura à direita, senão só a safra fechada manda e o negócio parado há um ano nunca entra na conta. Cada taxa é gravada com a data de corte, então dá para reproduzir o número que o gestor viu na semana passada. |
Decisões de projeto.
Merge guarda o id, não o dado pessoal
Abri mão de tabela limpa: o perdedor vira alias e o id antigo continua respondendo, então link e integração sobrevivem. Eliminação por titular esvazia os campos e mantém o ponteiro.
Árvore materializada, com versão que expira
Ler o pipeline de um gerente no topo de uma subárvore grande vira join dentro da política. Em troca, mudança de cargo invalida a versão e a leitura cai no caminho recursivo até o recálculo.
Duas curvas de forecast, não uma
Não troquei o vendedor pela taxa histórica: a tela mostra commit e cálculo lado a lado, e a diferença vira pauta. Custa uma conversa a mais; evita número único que ninguém defende.
Escopo.
O que o sistema cobre
- O fluxo de 8 passos e as decisões acima.
- As três telas desta página.
O que não está publicado
- Nome da empresa e números de operação não são divulgados aqui.
- O código não é público. O que dá para ver: o processo, as telas e as decisões.
Da mesma faixa.
Gestão e integração
Quer ver este sistema de perto?
Mostro o processo que mudou, os módulos, as telas e como foi construído.

