# ESPECIFICAÇÃO TÉCNICA 1.0 — VENDEDOR IA — REI DOS UNIFORMES

Status: aprovado para implementação, com uma correção de modelo aplicada nesta revisão (ver 0.1).

## 0.1 CORREÇÃO DE INCONSISTÊNCIA (aplicada nesta revisão, antes de qualquer implementação)

A versão anterior desta especificação misturava **custo** e **preço de venda** do produto: o texto dizia que o custo-base do produto "vive em `faixas_produto`", mas `faixas_produto` só tinha `preco_base_unitario` (que é preço de venda), e o Motor Comercial (seção 4, antiga) já tratava um `custo_produto` sem que existisse campo algum para armazená-lo. Isso tornava o modelo inconsistente e não implementável como estava escrito.

**Correção obrigatória, vigente a partir desta revisão:**

- `produtos` passa a ter um campo próprio **`custo_base_unitario` (numeric, nullable = NÃO DEFINIDO)** — o custo do produto, independente de qualquer preço de venda.
- `faixas_produto` continua existindo, mas seu `preco_base_unitario` é exclusivamente **preço-base de venda por faixa de quantidade**. `faixas_produto` nunca representa custo.
- `custos_tamanho_produto.custo_especial`, quando definido para o tamanho informado, **substitui** (não soma a) `custo_base_unitario` para efeito de cálculo de custo daquele item — é uma proteção de margem por tamanho (tamanhos maiores custam mais para produzir), não um valor adicional somado ao custo-base nem uma alteração de preço ao cliente.
- Logo:
  - `CUSTO_PRODUTO = custos_tamanho_produto.custo_especial(tamanho)` se existir, aplicável e não NULL para o tamanho informado; **senão** `produtos.custo_base_unitario`.
  - `VENDA_BASE_PRODUTO = faixas_produto.preco_base_unitario` da faixa aplicável à quantidade.
  - Nunca somar custo e preço de venda entre si, nem usar um no lugar do outro.
- Se o `CUSTO_PRODUTO` resultante (base ou especial) for NULL → margem não calculável → **BLOQUEADO**, pelo mesmo princípio já aplicado a personalizações (seção 5).

Esta correção está refletida em todas as seções abaixo (2.2, 4, 5) e na matriz de rastreabilidade. Nenhuma outra regra comercial foi alterada.

---

## 0.2 CORREÇÃO — Cliente novo/recorrente (aplicada na Fase 5, API)

**Histórico da decisão anterior (preservado, não descartado):** a versão inicial desta especificação
previa `clientes.is_novo (derivado, ver seção 13)` — um campo calculado a partir de um critério
numérico de "cliente recorrente" que nunca foi definido (ver seção 14, "Dados ainda faltantes"). Na
Fase 1 de implementação, isso foi resolvido provisoriamente como um campo manual simples,
`clientes.cliente_recorrente` (boolean, nullable), preenchido só por humano — funcional, mas
dependente inteiramente de preenchimento manual, sem derivação automática nenhuma.

**Correção aplicada agora:** a classificação novo/recorrente passa a ter três níveis de prioridade,
nunca inventando o critério numérico que continua NÃO DEFINIDO:

1. **Override humano explícito** (maior prioridade) — `clientes.cliente_recorrente_override`
   (renomeado do antigo `cliente_recorrente`), com auditoria completa via tabela `auditoria`
   (quem, quando, valor anterior, valor novo, motivo).
2. **Derivação automática válida** — ~~cliente com pelo menos 1 orçamento concluído (`status` em
   `aprovado`/`enviado`/`proposta_bling_gerada`) neste sistema → RECORRENTE~~ **(revisado — ver
   0.2.1 abaixo: esse critério estava errado, pois `status` de orçamento descreve o fluxo de
   cotação, não uma venda real)**.
3. **INDEFINIDO** — cliente sem override e sem evidência de venda confirmada neste sistema.
   Importante: isso **não** é tratado como "novo" automaticamente, porque o Rei dos Uniformes já
   tinha clientes recorrentes reais (ex. grupo ASA) antes deste sistema existir — "zero vendas
   confirmadas no sistema novo" não prova "nunca comprou antes". Um humano precisa confirmar via
   override quando isso importar ao cálculo (programa/arte).

Prioridade de resolução: **override humano > derivação automática > indefinido**. Quando o Motor
Comercial precisa da classificação (programa/arte, personalizado, quantidade < 10) e ela está
INDEFINIDA, o resultado é `PRECISA_ATENCAO` (motivo `CLASSIFICACAO_CLIENTE_NAO_DEFINIDA`) — nunca uma
suposição. Ver `src/repositorios/clientes.ts` (`resolverClienteRecorrente`) e
`PATCH /api/v1/clientes/:id/recorrencia`.

### 0.2.1 CORREÇÃO do critério de derivação automática (ainda na Fase 5, antes de avançar)

O critério do item 2 acima ("≥1 orçamento com status aprovado/enviado/proposta_bling_gerada") estava
**errado**: esses valores de `status` descrevem o fluxo interno de **cotação** (orçamento aprovado
internamente, orçamento enviado ao cliente) — nenhum deles prova que o cliente **comprou de fato**.
Um orçamento "enviado" pode nunca ter sido aceito.

**Critério corrigido:**

- `orcamentos` ganha `venda_confirmada_em` / `venda_confirmada_por` (migration 031) — um fato
  **ortogonal** ao `status` de cotação, marcado manualmente por um humano via
  `POST /api/v1/orcamentos/:id/confirmar-venda` só quando a venda é realmente confirmada (ex.:
  cliente pagou a entrada, confirmou a produção).
- Derivação automática = cliente com **≥1 orçamento com `venda_confirmada_em` preenchido** neste
  sistema → RECORRENTE. "Enviado" sozinho, isoladamente, **nunca** torna um cliente recorrente.
- Continua **não sendo** um critério numérico inventado (não é "3 pedidos"): é a mesma ideia de
  limite mínimo do termo, só que agora medindo evidência real de venda, não de cotação enviada.
- **Quando o Bling for integrado**, o histórico de pedidos/vendas de lá passa a ser a fonte
  principal — o ponto de extensão é só `resolverClienteRecorrente`, nenhuma outra parte do sistema
  muda.
- Critério numérico exato de "cliente recorrente" (quantas vendas confirmadas) continua **NÃO
  DEFINIDO**, propositalmente — ver seção 14.

---

## 0. PRINCÍPIO GERAL (não negociável)

A IA **interpreta e conversa**. O **backend é a única fonte de verdade** para preço, custo, desconto, margem, prazo, condição de pagamento e autorização de envio. Nenhuma regra comercial vive no prompt, no código do bot ou hardcoded. Todo cadastro comercial vive no PostgreSQL e é editável via painel, sem deploy.

## 0.3 CORREÇÃO — Modelo comercial consolidado (revisão pós-Fase 6, antes da Fase 7)

A premissa original de que `venda final = preço da peça + soma das personalizações` (usada em todo o texto original das seções 4 e 5 abaixo) **foi abandonada** — não representa como a Rei dos Uniformes forma preço de verdade. Substituída pelo modelo consolidado:

- **Produto personalizado configurado (core do negócio)**: peça + quantidade + configuração de aplicações (tipo, posição, dimensão) → preço decidido comercialmente, **nunca somado por fórmula**. O sistema calcula e expõe apenas o **custo** (peça + soma dos custos reais das aplicações); o preço de venda só existe quando (a) há autorização exata e ativa para aquela peça+configuração+faixa de quantidade com margem segura recalculada nos custos **atuais**, ou (b) um humano decide o preço para aquele pedido específico.
- **Peça lisa** (secundário): preço próprio e determinístico, por faixa de quantidade (`faixas_produto`) — só vale para produtos com `politica_personalizacao = opcional`; um produto `obrigatoria` nunca tem preço de peça lisa.
- **Personalização avulsa** (secundário): tabela de preço própria e determinística (`faixas_personalizacao.preco_venda_unitario`, reinterpretado — ver §2.3), mais cara, para serviço isolado (peça trazida pelo cliente, ou sem venda de peça pela Rei). Diferente do core, aqui **somar** o preço de vários serviços pedidos é o comportamento correto — é uma lista de serviços com preço próprio, não uma peça pronta com preço comercial único.

**Vocabulário fixo dos quatro estágios de conhecimento comercial** (nunca colapsados um no outro):
1. **Identificável** — o sistema monta a chave da configuração (`configuracao_hash`: peça + aplicações por tipo/posição/dimensão).
2. **Precedente** — a configuração já apareceu antes (via itens de orçamento ou histórico importado), com seu preço e origem preservados (decidido internamente / enviado / aprovado pelo cliente / venda confirmada / importado / fórmula antiga) — nunca colapsados em "já aconteceu uma vez".
3. **Validado** — ação humana **explícita e separada** de digitar o preço (`itens_orcamento.validado_em`/`validado_por`). Decidir um preço para fechar um pedido específico não promove sozinho a validado.
4. **Autorizado** — permissão explícita (`autorizacoes_comerciais`) para a IA reutilizar automaticamente um preço já validado, por peça+configuração+faixa de quantidade+escopo (geral/grupo/cliente, mesma hierarquia de `regras_comerciais`). Nasce sempre de um item validado; nunca de "já vendi uma vez".

Correspondência de configuração para **automação** é sempre **exata** (peça + mesmo conjunto de aplicações + mesma faixa de quantidade autorizada) — nunca por semelhança. "Parecido" só serve para mostrar precedentes de referência a um humano decidindo, jamais para autorizar uma ação automática. Hoje, a única dimensão com padrão comercial validado o suficiente para ser elegível a automação é bordado até 9cm; qualquer aplicação fora disso, e qualquer DTF (sem faixa comercial definida ainda), permanece identificável/registrável mas nunca elegível a autorização automática, mesmo que por engano exista uma autorização cadastrada para o mesmo hash.

Ver seções 4, 5 e 7 (atualizadas) para o detalhamento técnico desta correção.

---

## 1. ARQUITETURA

```
WhatsApp Cliente
   │  (Meta Cloud API oficial, webhook)
   ▼
API Gateway (Node.js/Express) ── auth, rate limit, validação de assinatura do webhook
   │
   ├── Módulo IA (camada de interpretação)
   │     - chama OpenAI (linguagem + visão) SOMENTE para: entender intenção,
   │       extrair entidades (produto, quantidade, personalização, aplicações),
   │       fazer triagem visual de arte/logo.
   │     - NUNCA calcula preço, custo ou margem.
   │     - NUNCA decide se pode enviar orçamento.
   │
   ├── Motor Comercial (serviço determinístico, puro, sem IA)
   │     - único componente autorizado a calcular preço/custo/margem/status.
   │     - usado por: painel, simulador, WhatsApp, orçamento, futura integração Bling.
   │
   ├── Serviço de Atendimento (máquina de estados)
   │     - orquestra: IA coleta → motor calcula → validação de margem →
   │       geração de orçamento → envio → registro.
   │
   ├── Serviço de Cadastros (CRUD: categorias, produtos, personalizações,
   │     faixas, clientes, grupos, regras, exceções)
   │
   ├── Serviço de Orçamentos/Snapshot
   │
   ├── Adaptador Bling (interface, implementação adiada — ver seção 8)
   │
   └── PostgreSQL (fonte de verdade de todos os cadastros e histórico)
```

Camadas:
- **Apresentação**: Painel web (SPA) consumindo API REST.
- **Aplicação**: Node.js (Express ou Fastify — a decidir na implementação, não afeta contrato de API).
- **Domínio**: Motor Comercial como módulo isolado, testável sem HTTP, sem IA, sem banco (funções puras recebendo dados já carregados).
- **Persistência**: PostgreSQL via ORM com suporte a migrations (Prisma ou Knex — a decidir na implementação; não é decisão comercial, fica a critério de quem implementar, mas migrations são obrigatórias independentemente da escolha).
- **Integrações externas**: Meta WhatsApp Cloud API, OpenAI API, Bling API (futuro).

Princípio anti-vendor-lock-in: qualquer chamada a OpenAI, Meta ou Bling passa por uma interface interna (`IAProvider`, `WhatsAppProvider`, `ERPProvider`). Trocar de fornecedor não deve exigir tocar no Motor Comercial nem no Serviço de Atendimento.

---

## 2. MODELO RELACIONAL POSTGRESQL

Convenções: `id` = UUID PK em todas as tabelas. Todas as tabelas comerciais têm `created_at`, `updated_at`, `created_by`, `ativo boolean default true` (soft delete / inativação, nunca DELETE físico em dado comercial).

### 2.1 Identidade e organização

**usuarios**
`id, nome, email, senha_hash, papel (admin | atendente), ativo, created_at, updated_at`

**grupos**
`id, nome (ex: "ASA"), observacoes, ativo, created_at, updated_at`

**clientes**
`id, nome, telefone (unique, identificador de contato), cnpj, grupo_id (FK grupos, nullable), condicao_especial (texto/JSON), observacoes, cliente_recorrente_override (boolean, nullable — override humano; ver §0.2), cliente_recorrente_override_motivo, cliente_recorrente_override_por (FK usuarios, nullable), cliente_recorrente_override_em, ativo, created_at, updated_at`
> Campo original `is_novo (derivado, ver seção 13)` substituído pela arquitetura de 3 níveis da §0.2
> (override > derivação automática > indefinido). A classificação EFETIVA (já resolvida) não é uma
> coluna — é calculada em tempo de leitura por `resolverClienteRecorrente` a partir do override acima
> e do histórico de `orcamentos`, para nunca ficar desatualizada.

**contatos**
`id, cliente_id (FK), telefone, nome_contato, cargo, observacoes, ativo`
(permite múltiplos telefones/pessoas por cliente/unidade, sem ambiguidade com o telefone principal de `clientes`)

### 2.2 Catálogo

**categorias**
`id, nome, ordem, ativo, created_at, updated_at`

**produtos**
`id, categoria_id (FK), nome, sku_interno (unique), referencia_meta, descricao_modelagem, quantidade_minima, custo_base_unitario (numeric, nullable = NÃO DEFINIDO), politica_personalizacao (enum: obrigatoria | opcional — default obrigatoria, dado que "produto sem personalização não pode ser orçado automaticamente"), observacoes, ativo, created_at, updated_at`
> `custo_base_unitario` é o **custo** do produto, independente de qualquer preço de venda. Campo próprio, nullable = NÃO DEFINIDO, bloqueia margem quando necessário e sem custo especial de tamanho aplicável (ver seção 0.1). O **preço-base de venda** por quantidade vive em `faixas_produto` (2.2) — nunca no mesmo campo que o custo.

**custos_tamanho_produto**
`id, produto_id (FK), tamanho (texto: P, M, G, GG, XXG, T6, T8, T10, EXG, 52, 54, 56...), custo_especial (numeric, nullable = NÃO DEFINIDO), ativo`
> Quando definido e aplicável ao tamanho informado, **substitui** `produtos.custo_base_unitario` no cálculo de custo daquele item (não soma). Protege margem para tamanhos que custam mais produzir; não implica preço diferente ao cliente — a venda continua vindo de `faixas_produto` (conforme especificado).

**faixas_produto**
`id, produto_id (FK), de (int), ate (int, nullable = infinito), preco_base_unitario (numeric, nullable = NÃO DEFINIDO), ativo, created_at, updated_at, versao (int)`
> `preco_base_unitario` é **exclusivamente preço-base de venda** por faixa de quantidade — nunca custo. Constraint de aplicação (validada em código, não apenas em banco): faixas do mesmo produto não podem se sobrepor; deve existir cobertura contínua sem buracos onde se espera venda; `ate` nulo apenas na última faixa.

### 2.3 Personalizações

**personalizacoes**
`id, nome (ex: "Bordado — Frente — até 9cm"), tipo (bordado | dtf | outro), descricao, ativo, created_at, updated_at`
> Regra financeira ≠ posição física: uma personalização pode ser aplicada em múltiplas posições físicas quando o custo/preço é idêntico (ex.: peito esquerdo e peito central usando a mesma regra "Bordado — Frente — até 9cm"). A posição física é registrada no item do orçamento (`aplicacoes_item`), não duplicada como nova personalização.

**faixas_personalizacao**
`id, personalizacao_id (FK), de (int), ate (int, nullable = infinito), custo_real_unitario (numeric, nullable = NÃO DEFINIDO), preco_venda_unitario (numeric, nullable = NÃO DEFINIDO), ativo, created_at, updated_at, versao (int)`
> Se `custo_real_unitario` for NULL e for necessário para validar margem → bloqueio automático (ver seção 5). **CORREÇÃO (§0.3):** `preco_venda_unitario` deixou de significar "preço de venda genérico" — desde a reformulação do modelo comercial, ele é usado **exclusivamente** quando a personalização é vendida avulsa (serviço isolado). Nunca é somado ao preço de um produto configurado; `custo_real_unitario` continua com o mesmo papel de sempre (custo interno, usado tanto no core quanto no avulso).

**aplicacoes_posicao** (catálogo auxiliar de posição — identidade da configuração comercial, não só exibição)
`id, nome, personalizacao_padrao_id (FK personalizacoes, nullable — sugestão), ativo`
> **CORREÇÃO (§0.3, migration 033):** catálogo normalizado para `Peito esquerdo`, `Peito direito`, `Peito central`, `Manga esquerda`, `Manga direita`, `Costas` (sem lateralidade em costas) — os nomes genéricos "Peito"/"Manga" usados até a Fase 6 eram inconsistentes demais para identificar uma configuração comercial de forma confiável (posição é hoje parte do `configuracao_hash`, ver seção 4). Posição vale igual para bordado e DTF — catálogo único, não um por técnica.

### 2.4 Regras comerciais

**regras_comerciais**
`id, tipo (pagamento | prazo | frete | troca_devolucao | margem_alvo | margem_minima | programa_arte), escopo (geral | grupo | cliente), grupo_id (FK, nullable), cliente_id (FK, nullable), parametros (JSONB), ativo, created_at, updated_at, versao`
> Prioridade de resolução: `cliente` > `grupo` > `geral`. Exemplos de `parametros`:
> - pagamento geral: `{"entrada_pct":50,"restante_pct":50,"restante_quando":"antes_ou_na_entrega"}`
> - pagamento ASA: `{"modelo":"integral_pos_entrega","prazo_dias_corridos":10}`
> - margem: `{"alvo_pct":40,"minima_pct":35}`
> - programa_arte: `{"valor":50,"aplica_se":"cliente_novo AND personalizado AND quantidade<10", "isento_se":"cliente_recorrente OR quantidade>=10"}`
> - prazo: `{"min_dias_uteis":5,"max_dias_uteis":20,"conta_a_partir_de":"aprovacao_condicoes"}`
> - troca_devolucao: `{"permite":false,"excecao":"erro_comprovado_producao"}`

**excecoes_comerciais**
`id, cliente_id (FK), tipo (mesmo enum de regras_comerciais), parametros (JSONB), motivo, ativo, created_at_by`
> Exceções pontuais por cliente, acima até de regra de cliente — mesmo nível de prioridade máxima; se houver mais de uma exceção ativa conflitante, o sistema deve bloquear e sinalizar "PRECISA DA MINHA ATENÇÃO" em vez de escolher arbitrariamente.

### 2.5 Atendimento e conversa

**atendimentos**
`id, cliente_id (FK, nullable até identificação), telefone_origem, status (enum — ver seção 3), modo_ia (herda global ou override), assumido_por (FK usuarios, nullable), assumido_em, proposta_bling_id (nullable, futuro), created_at, updated_at`

**mensagens**
`id, atendimento_id (FK), direcao (entrada|saida), autor (ia|humano|cliente), conteudo, tipo (texto|imagem|documento|audio...), timestamp, meta_message_id`

**anexos**
`id, atendimento_id (FK), mensagem_id (FK, nullable), tipo_arquivo (png|jpg|webp|pdf|svg), url_storage, triagem_ia (JSONB: resultado da análise visual, nullable), aprovado_producao (boolean, nullable — decisão humana, nunca da IA), created_at`

### 2.6 Orçamento (com snapshot imutável)

**orcamentos**
`id, atendimento_id (FK), cliente_id (FK), grupo_id (FK, nullable), status (rascunho|bloqueado_margem|aguardando_aprovacao_humana|aprovado|enviado|proposta_bling_gerada), margem_calculada_pct, regra_pagamento_aplicada (JSONB snapshot), regra_prazo_aplicada (JSONB snapshot), regra_programa_arte_aplicada (JSONB snapshot), valor_total, versao_tabela_referencia, created_at, updated_at, created_by`

**itens_orcamento**
`id, orcamento_id (FK), produto_id (FK), quantidade, tamanho (nullable — coletado depois, sem impacto no cálculo de preço-base por peça salvo custo especial), faixa_produto_snapshot (JSONB, nullable — só para peça lisa), custo_total_unitario_snapshot, venda_final_unitaria_snapshot, subtotal, origem_preco (nullable: calculado_motor | decidido_manualmente | autorizacao_automatica), configuracao_hash (texto, nullable — só produto configurado), validado_em (timestamp, nullable), validado_por (FK usuarios, nullable), configuracao_snapshot (JSONB, nullable — lista de aplicações com dimensão/custo, só produto configurado)`
> **CORREÇÃO (§0.3, migrations 034/037):** `origem_preco` responde só **de onde veio o número**; `validado_em`/`validado_por` respondem uma pergunta diferente — se o preço foi confirmado explicitamente como correto para virar conhecimento comercial. Decidir um preço (`origem_preco = decidido_manualmente`) nunca marca `validado_em` sozinho.

**personalizacoes_item**
`id, item_orcamento_id (FK), personalizacao_id (FK), aplicacao_posicao_id (FK, nullable), faixa_personalizacao_snapshot (JSONB: de/ate/custo/venda usados, ou {dimensao, custoRealUnitario} para produto configurado)`

**autorizacoes_comerciais** (migration 035, §0.3)
`id, produto_id (FK), configuracao_hash (texto), faixa_quantidade_de (int), faixa_quantidade_ate (int, nullable), preco_autorizado (numeric), escopo (geral|grupo|cliente), grupo_id (FK, nullable), cliente_id (FK, nullable), status (ativo|revogado), revogado_em, revogado_por (FK usuarios), motivo_revogacao, origem_item_orcamento_id (FK itens_orcamento, nullable), created_at, created_by`
> Mesmo formato de escopo/hierarquia de `regras_comerciais` — resolvida pela mesma função (`resolverRegra`). Nasce sempre de um item já validado (nunca de um item só decidido); nunca excluída fisicamente, só revogada (soft delete de decisão comercial).

**precedentes_importados** (migration 036, §0.3)
`id, produto_id (FK), configuracao_hash, configuracao_descricao_original (texto livre, para auditoria da reconstrução), quantidade, preco_praticado, data_referencia, origem_externa (ex.: "bling"), confiabilidade (venda_confirmada|orcamento_aprovado_cliente|orcamento_enviado|preco_validado_interno|desconhecida), observacao, created_at, created_by`
> Histórico externo (Bling, propostas antigas) mantido separado de `orcamentos`/`itens_orcamento` de propósito — um pedido antigo do Bling não tem atendimento nem conversa aqui. Tabela criada vazia; população depende da investigação (ainda pendente) de quais relatórios do Bling trazem pedido/item individualizado.

**snapshots_orcamento**
`id, orcamento_id (FK), payload_completo (JSONB — cópia integral de todas as faixas, regras e cálculos no momento da geração), created_at`
> Regra crítica: mudança futura na tabela de preços/custos **não altera** orçamentos antigos. O snapshot é a fonte de verdade para reimpressão/auditoria de qualquer orçamento já emitido.

### 2.7 Integrações e auditoria

**integracoes**
`id, tipo (whatsapp_meta|openai|bling), config (JSONB, sem segredos — segredos ficam em env vars), status, ultima_sincronizacao`

**mapeamento_catalogo_meta**
`id, referencia_meta (unique), produto_id (FK), created_at`

**auditoria**
`id, entidade, entidade_id, acao (create|update|inactivate), autor (FK usuarios, nullable se sistema), diff (JSONB before/after), created_at`

**versoes** (genérico, aplicável a `faixas_produto`, `faixas_personalizacao`, `regras_comerciais`)
`id, entidade, entidade_id, numero_versao, payload (JSONB), vigente_de, vigente_ate (nullable), created_by`

---

## 3. ESTADOS DO ATENDIMENTO

```
NOVO
  → IA_COLETANDO (IA pergunta apenas o que falta)
      → AGUARDANDO_CLIENTE (IA fez pergunta, aguarda resposta)
      → AGUARDANDO_ORCAMENTO (dados completos, motor calculando)
          → BLOQUEADO_PRECISA_ATENCAO (margem < mínima, dado NULL essencial,
             exceção conflitante, ou personalização ausente não resolvida)
              → (humano resolve) → AGUARDANDO_ORCAMENTO | ASSUMIDO_HUMANO
          → ORCAMENTO_ENVIADO
              → CONCLUIDO
              → (cliente responde) → IA_COLETANDO | ASSUMIDO_HUMANO
      → ASSUMIDO_HUMANO (a qualquer momento, via botão "Assumir atendimento")
          → devolvido → estado anterior antes da assunção
```

Regras de transição:
- Nenhuma transição para `ORCAMENTO_ENVIADO` é permitida sem que o Motor Comercial retorne `status = AUTORIZADO`.
- `ASSUMIDO_HUMANO` é absorvente até devolução explícita: a IA não responde no atendimento enquanto esse estado estiver ativo, independente do modo global.
- Filtros do painel (seção 7) mapeiam 1:1 para estes estados, exceto "Todos".

---

## 4. MOTOR COMERCIAL (determinístico, único, reutilizado)

**CORREÇÃO (§0.3):** o algoritmo original abaixo descrevia uma única função que sempre somava peça + personalizações para formar o preço de venda. Essa fórmula foi abandonada — não representa como o negócio decide preço (ver §0.3). O Motor Comercial hoje é **três funções puras**, cada uma com seu próprio contrato de entrada/saída, nunca reimplementadas fora de `src/motor-comercial/`:

**4.1 `calcularOrcamento` — peça lisa.** Entrada não tem (nem aceita, por assinatura de tipo) campo de personalização — garantia estrutural de que essa função nunca forma preço somando personalização, não uma questão de disciplina de uso. Só produtos com `politica_personalizacao = opcional` têm preço aqui; `obrigatoria` bloqueia sempre (`PERSONALIZACAO_AUSENTE`), por definição — essa peça nunca é vendida sem personalização. Resto do algoritmo como antes: seleciona faixa de `faixas_produto` pela quantidade, resolve custo (base ou especial por tamanho, nunca soma), calcula margem, resolve pagamento/prazo/programa-arte (sempre isento aqui, por exigir "personalizado").

**4.2 `calcularPersonalizacaoAvulsa` — personalização sem venda de peça pela Rei.** Tabela de preço própria e determinística (`faixas_personalizacao.preco_venda_unitario`, reinterpretado como preço avulso — §2.3). Ao contrário do core, aqui **somar** o preço de cada serviço avulso pedido é o comportamento correto.

**4.3 `avaliarConfiguracaoPersonalizada` — produto configurado (core).** Entrada: `produto_id, quantidade, [{personalizacao_id, aplicacao_posicao_id, dimensao}], preco_proposto (opcional, decisão humana), cliente_id, autorizacoes_candidatas (já pré-filtradas pelo chamador por produto+configuracao_hash+cobertura de quantidade, mesmo padrão de `regras_comerciais`)`. Algoritmo:
1. Calcula `custo_total_unitario = custo_produto + Σ custo_real_unitario(aplicações)` — igual de sempre, bloqueia se custo da peça ou de qualquer aplicação estiver ausente (`CUSTO_PRODUTO_NAO_DEFINIDO` / `CUSTO_PERSONALIZACAO_NAO_DEFINIDO` / `FAIXA_PERSONALIZACAO_NAO_ENCONTRADA`).
2. Se a configuração é **elegível para automação** (hoje: toda aplicação é bordado dentro do padrão validado até 9cm — nunca DTF, nunca bordado fora do padrão, mesmo que exista uma autorização cadastrada para o mesmo hash por engano) **e** existe autorização ativa cobrindo produto+configuração+quantidade+escopo (resolvida por `resolverRegra`, cliente > grupo > geral) **e** a margem recalculada com o **custo atual** (nunca o custo de quando a autorização foi criada) está segura → `AUTORIZADO_AUTOMATICO`, venda = preço autorizado.
3. Senão, se foi informado `preco_proposto` (decisão humana para este pedido) → `DECIDIDO_MANUALMENTE`, margem calculada sobre ele (decisão humana é respeitada mesmo com margem baixa — só sinaliza `MARGEM_ABAIXO_DO_MINIMO`, nunca bloqueia).
4. Senão → `AGUARDANDO_DECISAO_HUMANA`, só custo exposto, venda nula — nunca aproxima.
5. Programa/arte, pagamento e prazo resolvidos e anexados como sempre, independente do status de preço.

Cada função é chamada pelos mesmos pontos de entrada de antes (painel, simulador, fluxo de WhatsApp quando existir, geração de Orçamento, futura proposta Bling) — a bifurcação entre as três acontece na camada de rota (`/simulador/calcular`, `/orcamentos`), nunca dentro delas. **Proibido reimplementar qualquer uma fora deste módulo.**

---

## 5. REGRAS DE AUTORIZAÇÃO / BLOQUEIO

Bloqueiam envio automático de orçamento (todas são condições **OR** — qualquer uma já bloqueia):

| # | Condição | Status resultante |
|---|---|---|
| 1 | Produto exige personalização e nenhuma foi informada | BLOQUEADO |
| 2 | Faixa de quantidade sem cobertura no produto ou na personalização | BLOQUEADO |
| 3 | `preco_base_unitario` (venda, faixa do produto) ou `preco_venda_unitario` (personalização) NULL na faixa aplicável | BLOQUEADO |
| 4 | `custo_real_unitario` NULL na faixa de personalização aplicável (custo necessário à margem) | BLOQUEADO |
| 4b | Custo do produto NULL: `produtos.custo_base_unitario` sem `custos_tamanho_produto.custo_especial` aplicável para o tamanho informado (custo necessário à margem, ver 0.1) | BLOQUEADO |
| 5 | `margem_pct` < margem mínima resolvida (cliente > grupo > geral) | PRECISA_DA_MINHA_ATENÇÃO |
| 6 | Exceções comerciais conflitantes para o mesmo cliente/tipo | PRECISA_DA_MINHA_ATENÇÃO |
| 7 | IA em modo `IA_SOMENTE_COLETA` ou `IA_PAUSADA` | nunca autoriza envio, apenas coleta ou não responde |
| 8 | Atendimento em estado `ASSUMIDO_HUMANO` | IA não age |

Autorização de envio automático exige **AUTORIZADO** em todas as condições acima (1, 2, 3, 4, 4b, 5, 6, 7, 8). Qualquer bloqueio impede a transição para `ORCAMENTO_ENVIADO` mesmo que um humano "insista" via API — a única via de destravamento é o humano corrigir o dado faltante (cadastrar custo/preço) ou aprovar manualmente uma exceção, o que fica registrado em auditoria com autor.

**5.1 CORREÇÃO — Proteções específicas do produto configurado (§0.3).** Além das condições acima (que continuam valendo para peça lisa e avulso), o envio automático de um produto configurado exige **todas** as condições abaixo — qualquer uma ausente cai para decisão humana, nunca aproxima:

| # | Condição |
|---|---|
| a | Correspondência **exata** de peça + configuração canônica (`configuracao_hash`) + faixa de quantidade coberta pela autorização — nunca por semelhança |
| b | Configuração elegível para automação (hoje: toda aplicação é bordado dentro do padrão validado — nunca DTF, nunca bordado fora do padrão) |
| c | Autorização com `status = ativo` (revogada nunca é usada, mesmo que ainda apareça como candidata por engano) |
| d | Custo e margem recalculados com dados **vigentes** no momento do envio — nunca o custo/margem de quando a autorização foi criada |
| e | Margem recalculada acima da mínima resolvida (cliente > grupo > geral) — autorização ativa não basta se a margem atual ficou insegura |
| f | Nenhum sinal de negociação na conversa (pedido de desconto, contraproposta, "está caro", comparação com concorrente, condição diferente) — autonomia para **orçar** dentro do autorizado nunca vira autonomia para **negociar**; este portão vale mesmo dentro de configuração autorizada (ainda a implementar na camada de IA/WhatsApp — fora do escopo da Fase 6) |
| g | `modo_ia` do atendimento e o modo global respeitados como desligamento de emergência |
| h | Auditoria completa registrada no envio: motivo de considerar autorizado, autorização usada, preço aplicado, custos considerados, margem resultante, regras vigentes, quando/por quem a autorização foi criada |

---

## 6. APIs (contrato REST — nomes definitivos, payloads a detalhar na implementação)

Prefixo: `/api/v1`

**Autenticação**
- `POST /auth/login`
- `POST /auth/refresh`

**Cadastros — Categorias/Produtos**
- `GET/POST /categorias`, `PUT/DELETE /categorias/:id` (delete = inativar)
- `GET/POST /produtos`, `PUT/DELETE /produtos/:id`
- `GET/POST /produtos/:id/faixas`
- `GET/POST /produtos/:id/custos-tamanho`

**Cadastros — Personalizações**
- `GET/POST /personalizacoes`, `PUT/DELETE /personalizacoes/:id`
- `GET/POST /personalizacoes/:id/faixas`
- `GET/POST /aplicacoes-posicao`

**Composição de preço**
- `POST /simulador/calcular` → bifurca (§0.3/§4) entre peça lisa (sem personalização) e produto configurado (com personalização aplicada) antes de qualquer cálculo — nunca soma peça+personalização como preço. Mesmo endpoint usado pela tela central da seção 7.3.

**Validação e autorização (produto configurado, §0.3)**
- `POST /orcamentos/itens/:itemId/validar` — ação humana explícita, separada de decidir o preço (qualquer usuário autenticado)
- `POST /orcamentos/itens/:itemId/autorizar` — restrito a admin; exige item já validado; cria a autorização com faixa de quantidade e escopo informados por quem autoriza
- `GET /autorizacoes-comerciais` (filtros: status, produtoId)
- `GET /autorizacoes-comerciais/oportunidades` — configurações validadas 2+ vezes e ainda sem autorização ativa (o sistema só aponta a oportunidade; nunca se autoautoriza)
- `PATCH /autorizacoes-comerciais/:id/revogar` — restrito a admin; nunca exclui fisicamente

**Clientes/Grupos**
- `GET/POST /grupos`, `PUT/DELETE /grupos/:id`
- `GET/POST /clientes`, `PUT/DELETE /clientes/:id`
- `GET/POST /clientes/:id/excecoes`
- `PATCH /clientes/:id/recorrencia` — define/limpa o override humano de novo/recorrente (§0.2); auditado

**Regras comerciais**
- `GET/POST /regras-comerciais` (filtráveis por escopo/grupo/cliente)
- `PUT/DELETE /regras-comerciais/:id`

**Atendimentos**
- `GET /atendimentos` (filtros: status, busca por telefone/cliente/proposta)
- `GET /atendimentos/:id` (conversa completa + contexto comercial)
- `POST /atendimentos/:id/assumir`
- `POST /atendimentos/:id/devolver`
- `POST /atendimentos/:id/mensagens` (envio manual pelo humano)

**Orçamentos**
- `GET /orcamentos`, `GET /orcamentos/:id` (inclui snapshot)
- `POST /orcamentos` (gerado pelo fluxo, não por preenchimento livre de preço)
- `POST /orcamentos/:id/aprovar` (destrava um `PRECISA_DA_MINHA_ATENÇÃO`) — não implementado nesta
  fase (não estava no escopo dos 19 itens pedidos para a Fase 5; fica para quando o fluxo de
  aprovação humana ganhar tela própria)
- `POST /orcamentos/:id/enviar` — idem, não implementado nesta fase
- `POST /orcamentos/:id/confirmar-venda` (novo, §0.2.1) — marca a venda como confirmada; única fonte
  usada na derivação automática de cliente recorrente até o Bling ser integrado

**Controle da IA**
- `GET/PUT /config/modo-ia` (global: ATIVA | SOMENTE_COLETA | PAUSADA)

**Webhooks**
- `POST /webhooks/whatsapp` (validação de assinatura obrigatória)
- `POST /webhooks/openai` (se aplicável a callbacks assíncronos — a confirmar na implementação)

**Dashboard**
- `GET /dashboard/resumo` (contadores por status + negócios do período)

**Bling (interface prevista, não implementada nesta fase)**
- `POST /integracoes/bling/propostas` — adaptador, ver seção 8.

---

## 7. TELAS DEFINITIVAS

1. **Login**
2. **Dashboard** — "O que precisa da minha atenção?": contadores clicáveis (IA atendendo, Precisa de mim, Aguardando cliente, Aguardando orçamento, Orçamentos enviados, negócios do período). Cada contador leva à lista filtrada de atendimentos.
3. **Central de Atendimentos** — lista (cliente, telefone, última mensagem, horário, status, indicador de atenção), busca (telefone/cliente/proposta), filtros (Todos, IA atendendo, Aguardando cliente, Aguardando orçamento, Precisa de mim, Orçamento enviado, Concluído).
4. **Detalhe do Atendimento** — conversa completa + painel de contexto comercial (telefone, cliente, grupo, CNPJ, proposta Bling, produtos, quantidades, personalizações, aplicações, status, regra comercial, cálculo, margem, versão da tabela, anexos, logo, timestamps) + botão "Assumir atendimento".
5. **Produtos** — CRUD de categoria/produto, edição de faixas progressivas e custos por tamanho.
6. **Personalizações** — CRUD separado, edição de faixas (de/até/custo real/preço venda).
7. **Composição de Preço (tela central financeira)** — **CORREÇÃO (§0.3):** deixou de mostrar uma tabela consolidada com preço calculado por soma. Seleciona 1 produto + configuração de personalizações (tipo, posição, dentro/fora do padrão), mostra custo total calculado, um campo para decidir/confirmar o preço da configuração (ou o preço já vindo de autorização automática), margem resultante, e — depois de gerar o orçamento — ações para "Validar este preço" e, só admin, "Autorizar para automação" com faixa de quantidade e escopo. Peça lisa (sem personalização) e personalização avulsa continuam com preço direto por faixa, sem decisão manual. Esta tela chama o mesmo endpoint do simulador.
8. **Clientes e Grupos** — CRUD, vínculo de grupo, exceções.
9. **Regras Comerciais** — CRUD de margem alvo/mínima, pagamento, prazo, frete, troca/devolução, programa/arte, por escopo geral/grupo/cliente.
10. **Orçamentos** — lista, detalhe com snapshot, aprovação manual de bloqueados.
11. **Configurações / Controle da IA** — modo global (Ativa/Somente coleta/Pausada), mapeamento catálogo Meta → SKU.
12. **Auditoria/Histórico** — trilha de alterações por entidade.
13. **Autonomia da IA (nova, §0.3)** — central de revisão do conhecimento comercial autorizado, não tela de cadastro: autorizações ativas (produto, faixa de quantidade, preço, escopo, ação de revogar para admin), configurações recorrentes validadas 2+ vezes ainda sem autorização (o sistema aponta a oportunidade, nunca se autoautoriza), e histórico de revogações. Existe para responder "quanto do atendimento normal a IA já orça sozinha, e onde ainda dependo de mim?".

---

## 8. INTEGRAÇÕES

**Meta WhatsApp Business (Cloud API oficial)**
- Único canal permitido. Proibido WhatsApp Web automatizado ou biblioteca não oficial.
- Número atual `+55 21 99194-9993` não pode ser apagado, migrado ou desconectado sem confirmação explícita da Elenir.
- Objetivo declarado: Meta oficial + Coexistence, se elegível (elegibilidade é fator externo — ver riscos, seção 14).
- Webhook com validação de assinatura obrigatória (seção 9).
- Catálogo Meta é referência visual, não fonte de preço; mapeamento `referencia_meta → sku_interno` via tabela `mapeamento_catalogo_meta`, editável no painel.

**OpenAI**
- Usada exclusivamente para linguagem (interpretação de intenção/entidades) e visão (triagem de arte/logo).
- Análise da IA sobre logo **não** equivale a aprovação técnica final de produção; campo `aprovado_producao` em `anexos` é decisão humana, nunca setado automaticamente pela IA.
- Modelo/versão específica: NÃO DEFINIDO nesta fase — decisão de implementação, sem impacto em regra comercial.

**Bling (futuro)**
- Previsto por adaptador/interface (`ERPProvider`) desde o início do backend, para não exigir refatoração do Motor Comercial quando for ligado.
- Fluxo futuro: cliente → proposta → itens → serviços → valores → pagamento → número da proposta.
- Programa/arte (R$50) é linha de serviço separada também na proposta Bling — nunca embutida no preço da peça.
- Não simular ou fingir integração enquanto não existir: enquanto o adaptador Bling não estiver implementado, `proposta_bling_id` permanece NULL e nenhuma tela deve sugerir que existe proposta gerada.

---

## 9. SEGURANÇA

- Login com senha com hash (bcrypt/argon2 — detalhe de implementação) + papéis (`admin`, `atendente`).
- HTTPS obrigatório em produção (certificado gerenciado, ex. Let's Encrypt — detalhe de implementação).
- Segredos (chaves OpenAI, tokens Meta, credenciais Bling futuras) somente em variáveis de ambiente; nunca em repositório ou frontend.
- Validação de assinatura de webhook do WhatsApp (`X-Hub-Signature-256`) em toda requisição recebida.
- Backup diário do PostgreSQL (retenção a definir — NÃO DEFINIDO, depende de custo/infra, ver seção 13).
- Logs de aplicação e de auditoria (tabela `auditoria`) para toda alteração em cadastro comercial (quem, quando, o quê, valor antes/depois).
- Tratamento de erro centralizado: nenhuma falha de IA, WhatsApp ou Bling pode derrubar o Motor Comercial nem corromper um orçamento já com snapshot gerado.
- Rate limiting nas rotas públicas (webhook, login).

---

## 10. DEPLOY

- SO: Linux Ubuntu 24.04 LTS.
- Infra: VPS único, ~1 vCPU / 4 GB RAM / 50 GB NVMe (dimensionamento inicial; reavaliar se volume de atendimentos crescer — ver riscos).
- Processo Node.js gerenciado (ex. PM2 ou systemd — detalhe de implementação) + PostgreSQL na mesma VPS ou gerenciado (a decidir, sem impacto em regra comercial).
- Migrations aplicadas via pipeline manual ou script único de deploy (ex. `npm run migrate`) antes de subir a nova versão da aplicação.
- Variáveis de ambiente carregadas por arquivo `.env` fora do repositório, com permissões restritas.
- Sem multi-tenant: sistema é de uso interno do Rei dos Uniformes.

---

## 11. ESTRATÉGIA DE TESTES

- **Testes unitários do Motor Comercial** (prioridade máxima, cobrem os 11 casos obrigatórios da seção 12) — funções puras, sem mock de rede necessário além dos dados de cadastro.
- **Testes de integração de API** para CRUDs (produtos, personalizações, clientes, regras) garantindo que preço zero/vazio nunca vira gratuito.
- **Testes de máquina de estados** do atendimento (transições válidas e inválidas, em especial `ASSUMIDO_HUMANO` bloqueando a IA).
- **Testes de snapshot**: alterar uma faixa/regra após um orçamento existir e confirmar que o orçamento antigo não muda.
- **Teste de webhook**: assinatura inválida é rejeitada; payload válido gera/atualiza atendimento e mensagens corretamente.
- Sem mocks de "orçamento aprovado" que pulem o Motor Comercial — todo teste de fluxo completo passa pelo motor real com dados de teste.

---

## 12. TESTES OBRIGATÓRIOS DO MOTOR (conforme requisito, preservados integralmente)

1. Produto sem personalização → bloqueado.
2. Personalização sem custo necessário para margem → bloqueado.
3. Quantidade 30 → seleciona faixas corretas (produto e personalização).
4. Duas personalizações → soma custo e venda de ambas.
5. Margem < 35% → PRECISA_DA_MINHA_ATENÇÃO.
6. Cliente do grupo ASA → pagamento integral D+10.
7. Cliente normal (sem regra/grupo específico) → 50% entrada + 50% antes/na entrega.
8. Cliente novo + personalizado + quantidade < 10 → cobra R$50 de programa/arte.
9. Cliente recorrente → não cobra programa/arte.
10. Preço zero ou nulo em qualquer faixa → salvo como NÃO DEFINIDO, nunca enviado automaticamente.
11. Alterar tabela vigente após orçamento existente → orçamento antigo preserva snapshot original.

---

## 13. TESTE DE ACEITE FINAL (end-to-end)

Só considerar pronto quando, em ambiente real:

Cliente real manda mensagem no WhatsApp → produto é identificado (por catálogo Meta mapeado ou por texto) → IA pergunta somente o que falta (quantidade, personalização, aplicações, logo) → sem perguntar grade, modelagem, tecido já conhecido, cor, CEP, CPF/CNPJ ou tamanho de bordado → Motor Comercial seleciona faixas → calcula preço composto → aplica programa/arte quando cabível → identifica cliente/grupo → aplica regra de pagamento/prazo correta → valida margem → autoriza ou bloqueia orçamento → atendimento fica visível e clicável na Central → humano pode visualizar e assumir a qualquer momento → um restart do serviço não perde dados (tudo em PostgreSQL, nada em memória volátil que seja crítico) → histórico completo preservado (conversa, anexos, cálculo, regra usada, snapshot).

Bling entra em etapa própria, posterior a este teste de aceite.

---

## 14. DADOS AINDA FALTANTES (bloqueiam cálculo até serem preenchidos, não são bugs)

- Custos reais (custo de produção) de **todas** as personalizações, incluindo bordado padrão até 9cm (só a venda foi definida: 1–10→15, 11–40→12, 41+→8), bordado manga, bordado costas, DTF frente, DTF costas.
- Preço de venda e custo das demais personalizações citadas apenas como exemplo de nome (bordado manga, bordado costas, DTF frente, DTF costas) — nenhum valor numérico foi fornecido para elas.
- Definição operacional de "cliente recorrente" (a partir de quantas compras/pedidos, ou outro critério) — usado na regra do programa/arte mas nunca definido numericamente. **Continua NÃO DEFINIDO** mesmo após a §0.2: a derivação automática ali implementada só cobre o caso inequívoco (≥1 pedido concluído = recorrente); não define nenhum threshold numérico, e "0 pedidos neste sistema" fica INDEFINIDO (não "novo"), exigindo override humano.
- Retenção de backup (dias/semanas) e destino do backup.
- Modelo/versão exata da OpenAI a usar (linguagem e visão podem ser modelos diferentes).
- Credenciais/plano do Bling (para quando a integração for ligada).
- Definição de "restante antes/na entrega" — se há tolerância de dias ou é estritamente no ato.
- Frete: valor, forma de cálculo ou apenas texto "a combinar" — hoje é só "separado", sem regra.

**Adicionado na revisão do modelo comercial consolidado (§0.3):**
- Faixas comerciais de dimensão para DTF (tamanhos/áreas reais que a usuária pratica) — sem isso, nenhuma configuração de DTF é elegível para autorização automática, por decisão explícita (postura conservadora aceita).
- Percentual de tolerância para alertar divergência entre um preço proposto e o histórico da mesma configuração — deliberadamente não definido; a tela só mostra a divergência até haver volume real de dados.
- Regra de cálculo de margem quando um pedido autorizado tem grade de tamanho misturada (P/M/G/GG) — tendência da usuária é usar o custo real da composição daquele pedido específico, a confirmar quando esse cenário for implementado.
- Quais relatórios/exportações do Bling trazem pedido/item individualizado o suficiente para popular `precedentes_importados` — investigação ainda não concluída pela usuária.

## 15. RISCOS EXTERNOS

- Elegibilidade ao "Coexistence" da Meta depende de aprovação da própria Meta, fora do controle do projeto.
- Custos de conversação da Meta WhatsApp Cloud API (cobrança por conversa) e custos de uso da OpenAI (texto + visão) não foram tratados nesta especificação porque são financeiros — não incluir no MVP nenhum cálculo de custo operacional de IA/WhatsApp sem valor confirmado.
- Dimensionamento de VPS (1 vCPU/4GB) é apertado se o volume de atendimentos simultâneos crescer com uso de IA de visão; monitorar e revisar dimensionamento é responsabilidade operacional, não do escopo desta versão.
- Disponibilidade da API oficial da Meta (instabilidades, mudanças de política) e da API do Bling (quando integrada) são dependências externas fora do controle do sistema.
- Fila de atendimentos em "Precisa da minha atenção" depende de disponibilidade humana; sem SLA definido, atendimentos podem acumular sem alerta ativo (nenhuma notificação push/e-mail foi especificada nesta versão).

## 16. DECISÕES REALMENTE AINDA NÃO DEFINIDAS

- Framework HTTP (Express vs Fastify) e ORM (Prisma vs Knex) — sem impacto comercial, decisão livre de implementação.
- Local do PostgreSQL (mesma VPS vs. gerenciado) e do storage de anexos (disco local vs. objeto/S3-compatível).
- Mecanismo exato de fila/retentativa para mensagens do WhatsApp em caso de falha temporária da Meta.
- Notificações para humano quando um atendimento entra em "Precisa da minha atenção" (e-mail, push, nada) — não especificado.
- Papéis de usuário além de `admin`/`atendente` (ex.: perfil só-leitura) — não solicitado, não incluído.

## 17. CONTRADIÇÕES IDENTIFICADAS

Nenhuma contradição real foi encontrada no requisito original. Todos os pontos aparentemente incompletos (custos de personalização, definição de "recorrente", etc.) são lacunas de dado — tratadas na seção 14 — não contradições de regra.

---

## MATRIZ DE RASTREABILIDADE

| Requisito | Módulo | Entidade/Tabela | API | Tela | Teste |
|---|---|---|---|---|---|
| Produto sem personalização bloqueia preço | Motor Comercial | produtos, personalizacoes_item | POST /simulador/calcular | Composição de Preço; WhatsApp | Teste obrigatório #1 |
| Custo-base do produto independente do preço de venda (correção 0.1) | Cadastros/Motor | produtos.custo_base_unitario | GET/POST/PUT /produtos | Produtos; Composição de Preço | Teste adicional (custo produto NULL bloqueia) |
| Faixas progressivas de preço-base **de venda** | Cadastros/Motor | faixas_produto | GET/POST /produtos/:id/faixas | Produtos | Teste #3 |
| Custos especiais por tamanho (substituem custo-base) | Cadastros/Motor | custos_tamanho_produto | GET/POST /produtos/:id/custos-tamanho | Produtos | Cobertura em testes de margem |
| Personalizações com faixas custo/venda | Cadastros/Motor | personalizacoes, faixas_personalizacao | GET/POST /personalizacoes/:id/faixas | Personalizações | Teste #2, #4 |
| Custo NULL bloqueia margem | Motor Comercial | faixas_personalizacao | POST /simulador/calcular | Composição de Preço | Teste #2, #10 |
| Tela consolidada de composição de preço | Motor Comercial | faixas_produto, faixas_personalizacao | POST /simulador/calcular | Composição de Preço | Teste #3, #4 |
| Margem alvo/mínima configurável | Regras Comerciais | regras_comerciais (tipo=margem) | GET/POST /regras-comerciais | Regras Comerciais | Teste #5 |
| Programa/arte como linha separada | Motor Comercial/Orçamento | regras_comerciais (tipo=programa_arte), orcamentos | POST /orcamentos | Orçamentos; Regras Comerciais | Teste #8, #9 |
| Cliente novo/recorrente: override > automático > indefinido (§0.2) | Repositório de Clientes/Motor Comercial | clientes.cliente_recorrente_override*, orcamentos.status, auditoria | PATCH /clientes/:id/recorrencia | Clientes e Grupos | Testes de integração da Fase 5 (recorrência indefinida, override, auditoria) |
| Prioridade cliente > grupo > geral | Motor Comercial | clientes, grupos, regras_comerciais, excecoes_comerciais | GET/POST /regras-comerciais, /clientes/:id/excecoes | Clientes e Grupos; Regras Comerciais | Teste #6, #7 |
| ASA = D+10 | Regras Comerciais | regras_comerciais (escopo=grupo, grupo=ASA) | GET/POST /regras-comerciais | Clientes e Grupos | Teste #6 |
| Central de Atendimentos com filtros | Serviço de Atendimento | atendimentos | GET /atendimentos | Central de Atendimentos | Teste de máquina de estados |
| Assumir atendimento bloqueia IA | Serviço de Atendimento | atendimentos | POST /atendimentos/:id/assumir | Detalhe do Atendimento | Teste de máquina de estados |
| Controle global da IA | Serviço de Atendimento | integracoes/config | GET/PUT /config/modo-ia | Configurações | Teste de máquina de estados |
| Preço zero/vazio = NÃO DEFINIDO | Cadastros | faixas_produto, faixas_personalizacao | POST/PUT respectivos CRUDs | Produtos; Personalizações | Teste #10 |
| Snapshot de orçamento imutável | Orçamento | snapshots_orcamento, itens_orcamento, personalizacoes_item | GET /orcamentos/:id | Orçamentos | Teste #11 |
| Catálogo Meta = referência visual, não preço | Integração WhatsApp | mapeamento_catalogo_meta | Webhook /webhooks/whatsapp | Configurações | Teste de webhook |
| Triagem visual não é aprovação técnica | Integração OpenAI (visão) | anexos | POST /atendimentos/:id/mensagens | Detalhe do Atendimento | — |
| Bling como adaptador futuro | Integração Bling | integracoes | POST /integracoes/bling/propostas (não implementado) | Orçamentos (campo proposta_bling_id) | — |
| Segurança de webhook/segredos | Segurança | integracoes | POST /webhooks/whatsapp | — | Teste de webhook (assinatura inválida) |
| Auditoria de alterações comerciais | Auditoria | auditoria, versoes | — (transversal) | Auditoria/Histórico | — |

