# Inventário Cíclico — Configurações possíveis

Referência completa de tudo que dá pra configurar. A configuração vive **no plano**
(`inventario_planos`); o que é "estratégia" (universo, seleção, distribuição, esperado,
ajuste, política) é **plugável por nome** via registries — dá pra adicionar novas sem
tocar no motor.

---

## 1. Campos do plano (`inventario_planos`)

| Campo | Valores | Default | Obrigatório | Descrição |
|---|---|---|---|---|
| `nome` | texto | — | ✅ | Nome da campanha |
| `descricao` | texto | null | — | Descrição livre |
| `universo_provider` | `enderecos` \| `produtos` | `enderecos` | ✅ (registrado) | Quem monta o universo (§4) |
| `alvo_tipo` | `endereco` \| `produto` | derivado do provider | auto | Unidade do inventário |
| `universo_config` | JSON | null | — | Filtros do universo (§5) |
| `distribuicao_tipo` | `quantidade_fixa` \| `percentual` \| `periodo` | `quantidade_fixa` | ✅ | Como espalha a carga (§2) |
| `distribuicao_valor` | número | null | ✅ p/ fixa/percentual | N/dia (fixa) ou % (percentual) |
| `periodo_dias` | inteiro > 0 | null | ✅ p/ período | Cobrir tudo em N dias |
| `selecao_estrategia` | `sequencial` \| `aleatoria` \| `menos_recente` \| `prioridade_manual` | `sequencial` | ✅ (registrado) | Quais alvos pegar (§4) |
| `calendario_config` | JSON | null (= todo dia) | — | Quando rodar (§6) |
| `operadores_config` | JSON | null (= fila aberta) | — | Operadores designados (§7) |
| `lider_id` | inteiro | null | — | Líder da missão |
| `requer_aprovacao` | `0` \| `1` | `0` | — | **⚠️ ligue p/ o fluxo de conferente** (§3, §8) |
| `status` | `rascunho` \| `ativo` \| `pausado` \| `concluido` | `ativo`¹ | — | Só `ativo` entra no scheduler |
| `ativo` | `0` \| `1` | derivado do status | auto | Atalho de elegibilidade do cron |
| `campanha_inicio` | data | hoje | — | Início da campanha atual |
| `campanha_numero` | inteiro | `1` | auto | Incrementa quando o universo é 100% coberto |
| **`tolerancia`** | número ≥ 0 | `0` | — | Fase 2: \|contado−esperado\| ≤ tolerância ⇒ sem divergência |
| **`max_recontagens`** | inteiro | `1` | — | Fase 2: recontagens antes de escalar |
| **`auto_ajuste`** | `0` \| `1` | `0` | — | Fase 2: `0` = sempre exige conferente |
| **`recontagem_habilitada`** | `0` \| `1` | `1` | — | Fase 2: liga/desliga a recontagem automática |

¹ `CriarPlanoLaboratorio` cria como `ativo` por padrão; a coluna no banco tem default `rascunho`.

**Validação na criação** (`CriarPlanoValidator`): `nome` não-vazio · `universo_provider`
registrado · `distribuicao_tipo` válido · (`periodo`→`periodo_dias>0`; `fixa`/`percentual`→`distribuicao_valor>0`)
· `selecao_estrategia` registrada.

---

## 2. Distribuição (carga diária)

| `distribuicao_tipo` | Usa | Cálculo da carga/dia | Exemplo |
|---|---|---|---|
| `quantidade_fixa` | `distribuicao_valor` = N | N alvos/dia | `50` → 50/dia |
| `percentual` | `distribuicao_valor` = X | `ceil(total × X/100)` | `1` → 1%/dia |
| `periodo` | `periodo_dias` = D | `ceil(total ÷ D)` | `365` → 36.500/365 = 100/dia |

A carga é sempre limitada pela piscina elegível (se restam menos alvos, pega o que há).

---

## 3. Estratégias de seleção (qual alvo pegar)

| `selecao_estrategia` | Critério |
|---|---|
| `sequencial` | Ordem fixa (`ordem`) — cobertura previsível |
| `aleatoria` | Embaralha — dificulta previsão pelo operador |
| `menos_recente` | Nunca inventariados primeiro, depois os de inventário mais antigo |
| `prioridade_manual` | Maior `prioridade` primeiro (tiebreak `ordem`) |

> Futuro (precisam de dado não modelado): `curva_abc`, `maior_giro`, `maior_divergencia`,
> `familia`, `fornecedor` — entram via `SelecaoEstrategiaRegistry::registrar(nome, classe)`.

---

## 4. Universos (o que inventariar)

| `universo_provider` | `alvo_tipo` | Fonte | Filtros (`universo_config`) |
|---|---|---|---|
| `enderecos` | `endereco` | `wms_localizacoes` (ativas) | `codigo_prefixo` |
| `produtos` | `produto` | `produtos` (ativos) | `categoria_id` |

> Futuro: `familia`, `fornecedor`, `curva_abc` via `UniversoProviderRegistry::registrar(...)`.

---

## 5. `universo_config` (JSON, por provider)

```jsonc
// universo_provider = "enderecos"
{ "codigo_prefixo": "A-" }   // só endereços cujo código começa com "A-" (corredor/rua)

// universo_provider = "produtos"
{ "categoria_id": 12 }       // só produtos da categoria 12
```
Vazio/ausente = universo inteiro (todos os endereços ativos / todos os produtos ativos).

---

## 6. `calendario_config` (JSON — quando rodar)

```jsonc
{
  "dias_semana": [1, 2, 3, 4, 5],   // ISO: 1=seg … 7=dom. Presente ⇒ só nesses dias
  "apenas_dias_uteis": true,         // pula sábado(6) e domingo(7)
  "feriados": ["2026-12-25", "2027-01-01"], // não roda nessas datas
  "horario": "07:00"                 // informativo (a hora real é a do cron, 04:00)
}
```
Sem config = **roda todo dia**. Os filtros são combináveis (feriado vence; depois dias úteis; depois dias_semana).

---

## 7. `operadores_config` (JSON — quem executa)

```jsonc
[]                                  // fila aberta (qualquer operador pega)
[12, 34]                            // designada: operadores 12 e 34
[{ "id": 12, "andar": 1 }]          // designada com andar
```

---

## 8. Política de divergência (fase 2)

| Campo | Efeito |
|---|---|
| `tolerancia` | Diferença ignorável. `0` = qualquer diferença vira divergência |
| `recontagem_habilitada` | `1` = gera recontagem automática; `0` = escala direto |
| `max_recontagens` | Quantas recontagens antes de escalar ao conferente |
| `auto_ajuste` | `0` (default e único suportado nesta fase) = nada ajusta sem conferente |

**⚠️ Requisito do fluxo de conferente:** para o escalonamento reter a contagem no gate do
AlocacaoWms, o plano precisa de **`requer_aprovacao = 1`** (a contagem nasce `pendente`; o
conferente decide em `RevisarMovimentos`; a varredura aplica o ajuste no que ele aprovar).
Sem isso, a divergência é detectada e gera recontagem, mas o ajuste só ocorre após aprovação.

**Política plugável:** `RecontagemPoliticaRegistry` (hoje só `padrao`). Regra do `padrao`:
recontagem que confirma a contagem anterior → escala; senão reconta até `max_recontagens`; depois escala.

---

## 9. Seams plugáveis (interno hoje → WMS externo no futuro)

| Registry | Hoje | Futuro |
|---|---|---|
| `EsperadoProviderRegistry` | `local` (saldo interno) | `wms` |
| `AjusteAplicadorRegistry` | `interno` (correção no saldo) | `wms` (push) |

Trocar = `registrar(nome, classe)` + apontar o nome — o motor não muda.

---

## 10. Estados (enums) — valores possíveis

| Enum | Valores |
|---|---|
| `PlanoStatus` | `rascunho` · `ativo` · `pausado` · `concluido` |
| `CicloStatus` | `agendado` · `em_andamento` · `concluido` · `parcial` · `cancelado` |
| `CicloTipo` | `normal` · `recontagem` |
| `DivergenciaStatus` | `aberta` · `recontagem` · `escalada` · `ajustada` · `resolvida` · `descartada` |
| `AlvoTipo` | `endereco` · `produto` |

---

## 11. Exemplos prontos (payload de criação)

```jsonc
// "Inventariar 100% do estoque em 1 ano"
{
  "nome": "Inventário Geral Anual",
  "universo_provider": "enderecos",
  "distribuicao_tipo": "periodo",
  "periodo_dias": 365,
  "selecao_estrategia": "menos_recente",
  "calendario_config": { "apenas_dias_uteis": true }
}

// "500 endereços por semana, só do corredor A, com recontagem e conferente"
{
  "nome": "Cíclico Corredor A",
  "universo_provider": "enderecos",
  "universo_config": { "codigo_prefixo": "A-" },
  "distribuicao_tipo": "quantidade_fixa",
  "distribuicao_valor": 100,
  "selecao_estrategia": "sequencial",
  "calendario_config": { "dias_semana": [1,2,3,4,5] },
  "requer_aprovacao": 1,
  "tolerancia": 1,
  "max_recontagens": 1,
  "recontagem_habilitada": 1
}

// "1% do estoque por dia, aleatório"
{
  "nome": "Amostral 1%/dia",
  "universo_provider": "enderecos",
  "distribuicao_tipo": "percentual",
  "distribuicao_valor": 1,
  "selecao_estrategia": "aleatoria"
}
```

---

## 12. Onde cada config é lida (rastreabilidade)

| Config | Lida em |
|---|---|
| distribuição | `Distribuicao/DistribuicaoFactory` + estratégias |
| seleção | `Selecao/SelecaoEstrategiaRegistry` + estratégias |
| universo / `universo_config` | `Universos/*Provider` |
| `calendario_config` | `Calendario/Calendario::deveRodarHoje` |
| `operadores_config` / `requer_aprovacao` | `Bridge/MissaoInventarioBridge` (na criação da missão) |
| `tolerancia` | `Laboratorios/AvaliarDivergenciaLaboratorio` |
| `max_recontagens` / `recontagem_habilitada` | `Politica/PadraoRecontagemPolitica` (via `ProcessarDivergenciasCicloLaboratorio`) |
| `auto_ajuste` | reservado (sempre conferente nesta fase) |
| criação/validação | `Laboratorios/CriarPlanoLaboratorio` + `Validators/CriarPlanoValidator` |
