# REQUIREMENTS.md — VENDEDOR IA — REI DOS UNIFORMES

> Fonte completa: ESPECIFICACAO_TECNICA_1.0.md. Este arquivo é o resumo executável. Em caso de dúvida, a especificação completa prevalece.

## Correção de modelo aplicada (ver especificação §0.1)
A especificação original misturava custo e preço de venda do produto (dizia que o custo vivia em `faixas_produto`, que só tem `preco_base_unitario` de venda). Corrigido: `produtos.custo_base_unitario` (nullable) é o custo do produto, independente do preço. `faixas_produto.preco_base_unitario` é exclusivamente preço-base de **venda** por quantidade. `custos_tamanho_produto.custo_especial`, quando aplicável ao tamanho, **substitui** (não soma) o custo-base. Se o custo do produto (base ou especial) for NULL → margem não calculável → BLOQUEADO, mesmo com venda definida.

## Correção de arquitetura: cliente novo/recorrente (ver especificação §0.2/§0.2.1, Fase 5)
Decisão anterior (Fase 1, preservada como histórico): campo manual único `clientes.cliente_recorrente`, só preenchido por humano, sem derivação nenhuma. Corrigido na Fase 5: prioridade **override humano (auditável) > derivação automática > indefinido**.

Derivação automática — **critério corrigido em §0.2.1** (a primeira versão contava orçamentos com status aprovado/enviado, o que estava errado: esses status descrevem o fluxo de cotação, não uma venda real). Critério atual: cliente com **≥1 orçamento com `venda_confirmada_em` preenchido** (marcado manualmente via `POST /orcamentos/:id/confirmar-venda`, ortogonal ao `status` de cotação) neste sistema → recorrente. "Enviado" sozinho NUNCA torna recorrente. Isso não é um threshold numérico inventado, é o limite mínimo do termo (evidência real de compra vs. nenhuma). Quando o Bling for integrado, o histórico de pedidos/vendas de lá passa a ser a fonte principal.

Zero vendas confirmadas → **INDEFINIDO** (não "novo" automaticamente — o negócio já tinha clientes recorrentes reais antes deste sistema existir). Critério numérico exato ("X pedidos") **continua NÃO DEFINIDO**, propositalmente. Quando a classificação importa ao cálculo (programa/arte) e está indefinida → `PRECISA_ATENCAO`, nunca inventado.

## Princípio inegociável
IA interpreta e conversa. IA **nunca** calcula preço/custo/margem e **nunca** decide se pode enviar orçamento. O Motor Comercial (backend, determinístico) é a única fonte de verdade. Nada comercial hardcoded — tudo em PostgreSQL, editável sem deploy.

## Stack
- Backend: Node.js. Banco: PostgreSQL + migrations obrigatórias.
- IA: OpenAI (linguagem + visão) apenas para interpretação/extração de entidades e triagem visual — nunca para cálculo.
- WhatsApp: Meta Cloud API oficial exclusivamente. Proibido WhatsApp Web/libs não oficiais. Número atual `+55 21 99194-9993` — não remover/migrar sem confirmação da Elenir.
- Bling: adaptador/interface previsto desde já, implementação adiada. Não simular integração inexistente.
- Deploy: Ubuntu 24.04, VPS ~1vCPU/4GB/50GB NVMe.
- Todas as integrações externas (IA, WhatsApp, ERP) atrás de interfaces internas — anti vendor lock-in.

## Regra fundamental
Produto + quantidade sem personalização definida → **preço bloqueado**. IA só pergunta o que falta e afeta o cálculo: produto, quantidade, bordado/estampa, aplicações/locais, logo/arte. Nunca perguntar automaticamente: grade/tamanho, modelagem, tecido já conhecido, cor, CEP, CPF/CNPJ, tamanho do bordado.

## Motor Comercial — algoritmo (função pura, único lugar da lógica, usado por painel/simulador/WhatsApp/orçamento/Bling)
Para quantidade Q e produto+personalizações:
1. Sem personalização exigida e nenhuma informada → BLOQUEADO.
2. Selecionar faixa do produto (de/até cobrindo Q) → dá `preco_base_produto` (VENDA). Sem faixa ou `preco_base_unitario` NULL → BLOQUEADO.
3. Para cada personalização: selecionar faixa (de/até cobrindo Q). `preco_venda_unitario` NULL → BLOQUEADO. `custo_real_unitario` NULL → BLOQUEADO (margem não calculável, mesmo com venda definida).
4. Determinar `custo_produto` (independente do preço de venda): se tamanho informado e existir `custo_especial` aplicável e não NULL para esse tamanho → usar `custo_especial` (substitui, não soma); senão usar `produtos.custo_base_unitario`. Se o resultado for NULL → BLOQUEADO (margem não calculável).
5. `custo_total_unitario = custo_produto + Σ custo_personalizacoes`
   `venda_final_unitaria = preco_base_produto + Σ preco_venda_personalizacoes`
   `total = venda_final_unitaria × Q`
   `margem_pct = (venda_final_unitaria - custo_total_unitario) / venda_final_unitaria × 100`
6. Margem < mínima resolvida (cliente > grupo > geral) → status PRECISA_DA_MINHA_ATENÇÃO, não envia automático.
7. Programa/arte é **linha de serviço separada**, nunca somada ao preço da peça: R$50 se cliente novo + personalizado + quantidade < 10; isento se cliente recorrente OU quantidade ≥ 10.
8. Resolver pagamento/prazo (cliente > grupo > geral) e anexar como snapshot ao orçamento, sem recalcular preço.
9. Retornar: status (AUTORIZADO | BLOQUEADO | PRECISA_ATENCAO), motivo, tabela consolidada (faixa, custo produto, venda produto, custo personalizações, venda personalizações, custo total unitário, venda final unitária, margem%, status), total, programa_arte, pagamento, prazo.

## Regras comerciais atuais (seed inicial — configuráveis, não hardcode)
- Margem alvo 40%, mínima automática 35%.
- Pagamento geral: 50% entrada + 50% antes/na entrega.
- Grupo ASA: pagamento integral D+10 (10 dias corridos após entrega).
- Prazo geral: 5–20 dias úteis após aprovação/condições necessárias.
- Frete: separado (sem regra de cálculo definida).
- Personalizados: sem troca/devolução salvo erro comprovado de produção.
- Programa/arte: R$50 (regra descrita acima).
- Prioridade de resolução de regra: cliente > grupo > regra geral.

## Dados de catálogo (seed — não inventar preços além destes)
Custos (estes valores são CUSTO, não preço de venda — preço de venda ao cliente vem de `faixas_produto.preco_base_unitario`, ainda NÃO DEFINIDO/a confirmar, portanto orçamento automático fica BLOQUEADO por preço de venda ausente até essas faixas serem cadastradas). O valor "base" de cada produto vai em `produtos.custo_base_unitario`; os valores por tamanho diferentes do base vão em `custos_tamanho_produto.custo_especial` (substituem o base para aquele tamanho, não somam):
- POLO BÁSICA: custo_base (P–GG) 24; especiais XXG 26, T6 41, T8 46, T10 51. Modelagem masc/fem.
- POLO PREMIUM PIQUET: custo_base (P/M/G/GG) 32; especiais EXG 40; 52/54/56 45. Unissex.
- T-SHIRT 100% POLIÉSTER: custo_base (P/M/G/GG) 14; especiais XXG 15; T6 21; T8 26; T10 31.
- T-SHIRT 100% ALGODÃO: custo_base (P/M/G/GG) 15,90; especial EXG 19,90.
- MALHA PV 65/35: custo_base (P/M/G/GG) 17,90; especial EXG 23.
- DRY-FIT: custo_base (P/M/G/GG) 17; especial EXG 22.

Bordado padrão até 9cm — **venda** (custo real ainda NÃO definido, bloqueia margem até cadastro):
- 1–10: venda 15 | 11–40: venda 12 | 41+: venda 8.

Demais personalizações citadas (bordado manga, bordado costas, DTF frente, DTF costas) — sem valores; cadastrar quando definidos.

## Modelo de dados (ver especificação completa §2 para todos os campos)
Tabelas: usuarios, grupos, clientes, contatos, categorias, produtos, custos_tamanho_produto, faixas_produto, personalizacoes, faixas_personalizacao, aplicacoes_posicao, regras_comerciais, excecoes_comerciais, atendimentos, mensagens, anexos, orcamentos, itens_orcamento, personalizacoes_item, snapshots_orcamento, integracoes, mapeamento_catalogo_meta, auditoria, versoes.
Regra: preço zero/vazio → salvar como NULL/NÃO DEFINIDO, nunca tratar como grátis, IA nunca envia preço não definido. Soft delete (campo `ativo`) em tudo comercial, nunca DELETE físico.
Correção de modelo: `produtos.custo_base_unitario` é o custo do produto (nullable); `faixas_produto.preco_base_unitario` é só preço-base de venda por quantidade; `custos_tamanho_produto.custo_especial` substitui o custo-base para o tamanho aplicável. Custo e venda nunca se misturam no mesmo campo.
Snapshot: todo orçamento grava payload completo (faixas, custos, regras usadas). Mudar tabela vigente depois **não** altera orçamento antigo.

## Estados do atendimento
`NOVO → IA_COLETANDO → (AGUARDANDO_CLIENTE | AGUARDANDO_ORCAMENTO) → (BLOQUEADO_PRECISA_ATENCAO | ORCAMENTO_ENVIADO) → CONCLUIDO`, com `ASSUMIDO_HUMANO` absorvente a qualquer momento (IA para de responder até devolução). Modos globais de IA: ATIVA | SOMENTE_COLETA | PAUSADA.

## Telas obrigatórias
Login; Dashboard ("o que precisa da minha atenção", contadores clicáveis); Central de Atendimentos (lista + busca + filtros: Todos, IA atendendo, Aguardando cliente, Aguardando orçamento, Precisa de mim, Orçamento enviado, Concluído); Detalhe do Atendimento (conversa + contexto comercial completo + botão Assumir); Produtos (CRUD + faixas + custos por tamanho); Personalizações (CRUD + faixas); **Composição de Preço** (tela financeira central: produto + N personalizações → tabela consolidada); Clientes e Grupos (CRUD + exceções); Regras Comerciais (CRUD por escopo); Orçamentos (lista + snapshot + aprovação manual); Configurações (modo IA, mapeamento catálogo Meta); Auditoria.

## APIs principais
Ver especificação completa §6. Prefixo `/api/v1`. Endpoint central: `POST /simulador/calcular` (chama o Motor Comercial — mesmo endpoint usado pela tela de Composição de Preço).

## Segurança
Login com hash de senha + papéis (admin/atendente); HTTPS; segredos só em env vars; validação de assinatura do webhook Meta; backup do Postgres; logs e auditoria de toda alteração comercial (quem, quando, antes/depois); tratamento de erro que nunca corrompe snapshot já gerado.

## Testes obrigatórios do motor (todos devem passar)
1. Produto sem personalização → bloqueado. 2. Personalização sem custo → bloqueado. 3. Quantidade 30 → faixas corretas. 4. Duas personalizações → soma ambas. 5. Margem <35% → precisa atenção. 6. ASA → D+10. 7. Cliente normal → 50/50. 8. Cliente novo + personalizado + qtd<10 → R$50. 9. Cliente recorrente → não cobra. 10. Preço zero/nulo → NÃO DEFINIDO, não envia. 11. Alterar tabela → orçamento antigo preserva snapshot.

## Teste de aceite final
Cliente real no WhatsApp → produto identificado → IA pergunta só o necessário → motor calcula com faixas corretas → programa/arte aplicado corretamente → cliente/grupo identificado → regra de pagamento/prazo correta → margem validada → orçamento aprovado ou bloqueado → atendimento visível/clicável na Central → humano assume quando quiser → restart não perde dados → histórico completo preservado. Bling integra em etapa posterior.

## NÃO DEFINIDO / bloqueado até novo dado (não inventar)
Custos reais de todas as personalizações (inclusive bordado padrão); preço e custo de bordado manga/costas e DTF frente/costas; definição numérica exata de "cliente recorrente" (ex. "X pedidos" — a derivação automática da §0.2 cobre só o caso inequívoco ≥1 pedido concluído, não define esse número); retenção/destino de backup; modelo exato da OpenAI; credenciais/plano Bling; regra de tolerância em "antes/na entrega"; regra de cálculo de frete.

## Decisões livres de implementação (sem impacto comercial)
Express vs Fastify; Prisma vs Knex; local do Postgres e do storage de anexos; mecanismo de retry de mensagens; notificações de "precisa de atenção"; papéis de usuário além de admin/atendente.

## Contradições identificadas
Nenhuma. Lacunas de dado (seção "NÃO DEFINIDO") não são contradições.
