# 08 — Contrato das ações novas do wrapper (módulo Contratos)

Fase 0 do módulo Contratos (`docs/CONTRATOS-PLANO.md`). Acrescenta duas ações novas à
mesma família de `docs/03-CONTRATO-WRAPPER-CT107.md`, ambas na **chave de consulta**
(nenhuma escrita ao Sabichão). Mesmas regras gerais do documento 03: entrada JSON por
stdin, saída JSON por stdout, stderr nunca com dados sensíveis, password resolvida
sempre dentro do wrapper (secção 8 do documento 03).

## `consulta.fila_contratos`

**Efeito**: `SELECT` a `processo` com `contrato='Fazer' AND estado='Ativo'`, juntando o
suficiente para a lista da fila da app (sem os dados completos do contrato — isso é
`consulta.dados_contrato`).

**Entrada**
```json
{ "acao": "consulta.fila_contratos" }
```
Sem parâmetros — devolve sempre a fila inteira (é pequena, mesma ordem de grandeza que
a fila de Fichas).

**SQL equivalente** (o que o wrapper corre, réplica de `resolver_id()`/consulta livre da
skill, mas listando toda a fila em vez de filtrar por id/mai/nome):
```sql
SELECT id_processo, nome, mai, zona1, admissao, contrato, estado
FROM processo
WHERE contrato = 'Fazer' AND estado = 'Ativo'
ORDER BY admissao ASC;
```

**Saída (sucesso)**
```json
{
  "ok": true,
  "acao": "consulta.fila_contratos",
  "exit_code": 0,
  "resultado": {
    "processos": [
      {
        "id_processo": 851,
        "nome": "Ana Sofia Martins",
        "mai": "157459",
        "zona": "Porto",
        "admissao": "2026-10-01"
      }
    ]
  }
}
```
`contrato`/`estado` não vão para o resultado (são sempre `Fazer`/`Ativo`, é o próprio
filtro do SQL — incluir seria redundante e não acrescenta nada à app).

**Exit codes**: 0 = sucesso (mesmo lista vazia); 1 = falha de SQL/ligação.

## `consulta.dados_contrato`

**Efeito**: réplica de `carregar()` + os dados brutos de que `calcular()` precisa na
skill (`~/.claude/skills/fazer-contrato/gerar_contrato.py`) — processo, contrato,
afetação de cliente mais recente (com aviso se houver mais de uma ativa), serviços do
cliente, cliente indicado por flag (se aplicável), candidatos a cliente (heurística por
nome), categorias salariais, ordenado mensal, configuração `contrato_ft_horas_semana`,
feriados. **Não calcula** (isso é responsabilidade do motor PHP, `App\Services\Contratos`,
Fase 2) — devolve os dados brutos na mesma forma multi-tabela que `carregar()` devolve
hoje (dict de listas de linhas), para o motor os consumir exatamente como consome hoje o
resultado de `sql_prod()`. Isto mantém o wrapper "burro" (só leitura, sem lógica de
negócio) e a lógica de negócio (que campo vem de onde, motivo do termo, vencimento, etc.)
inteiramente do lado do CT assistente, testável sem SSH.

**Entrada**
```json
{ "acao": "consulta.dados_contrato", "id_processo": 851, "cliente": "" }
```
- `id_processo`: obrigatório, inteiro.
- `cliente`: opcional, `numero_contrato` a forçar (equivalente à flag `--cliente` da
  skill) — normalmente vazio; só usado quando o utilizador já respondeu à pergunta
  pendente "cliente" numa corrida anterior e está a recalcular.

**SQL equivalente** — as mesmas 9 queries de `carregar()` em `gerar_contrato.py`
(linhas 205-238), pela mesma ordem, substituindo `{idp}` e `{cli_flag}` pelos parâmetros
de entrada (escapados/parametrizados, nunca concatenação livre):
1. `processo` — dados do funcionário (nome, morada, documento, nif, niss, mai,
   especialidades, validades de cartão, admissão, zona, local_servico, nhoras,
   contrato, estado, nacionalidade) por `id_processo`.
2. `processo_contrato` — modalidade, datas, duração+unidade, renovação+unidade,
   motivo_termo, nhoras/periodo_horas, regime, por `id_processo`.
3. `cliente_equipa` + `clientes` — afetações ativas (`ativa=1`) do processo, mais
   recente primeiro (`data_inicio DESC`).
4. `clientes_servicos` — serviços dos clientes das afetações ativas (ou do `cliente`
   forçado).
5. `clientes` pelo `numero_contrato` do `cliente` forçado (se dado).
6. `clientes` candidatos por semelhança de nome/abreviatura com `processo.local_servico`
   (`LIKE`, limite 12).
7. `processamento_categorias` — todas as linhas (o motor escolhe o ano).
8. `processamento_config` — todas as linhas (idem).
9. `configuracoes_globais` — só a chave `contrato_ft_horas_semana`.
10. `feriados` — tabela inteira (pequena, sem filtro de ano; o motor filtra os anos que
    precisa, tal como a skill).

**Saída (sucesso)** — mesma forma que a skill usa internamente (`rs['processo']`,
`rs['contrato']`, etc.), cada chave uma lista de objetos (mesmo quando é uma linha só,
para o motor tratar tudo com o mesmo código que hoje trata o resultado do SSH):
```json
{
  "ok": true,
  "acao": "consulta.dados_contrato",
  "exit_code": 0,
  "resultado": {
    "processo": [ { "id_processo": 851, "nome": "...", "morada": "...", "ncc": "...",
      "validade_cc": "2035-01-22", "nif": "...", "niss": "...", "mai": "157459",
      "especialidades": "VIG/ARD/ARE", "val_vig": "2028-04-26", "val_spr": null,
      "val_ard": "2028-06-16", "val_are": "2028-06-16", "val_outro": null,
      "admissao": "2026-10-01", "zona1": "Porto", "local_servico": "...",
      "nhoras": "40", "contrato": "Fazer", "estado": "Ativo", "nacionalidade": "..." } ],
    "contrato": [ { "modalidade": "termo_certo", "data_inicio": "2026-10-01",
      "data_fim": null, "duracao_meses": "6", "duracao_unidade": "meses",
      "renov_duracao": null, "renov_duracao_unidade": null, "motivo_termo": "e) ...",
      "nhoras": null, "periodo_horas": null, "regime": null } ],
    "equipa": [ { "numero_contrato": "1234", "data_inicio": "2026-09-01",
      "data_fim": null, "designacao_comercial": "...", "morada_servico": "...",
      "estado": "Ativo" } ],
    "servicos": [ { "numero_contrato": "1234", "especialidade": "Vigilante",
      "quantidade": "1", "dias": "...", "horario": "..." } ],
    "cliente_flag": [],
    "candidatos": [],
    "categorias": [ { "ano": "2026", "categoria": "Vigilante", "valor_hora": "5.98" } ],
    "ordenado": [ { "ano": "2026", "ordenado_mensal": "1015.95" } ],
    "cfg": [ { "chave": "contrato_ft_horas_semana", "valor": "40" } ],
    "feriados": [ { "data": "2026-12-25" } ]
  },
  "avisos": []
}
```
`avisos` aqui é só de infraestrutura (ex. "processo não encontrado"), não os avisos de
negócio que `calcular()` produz (esses são responsabilidade do motor, não do wrapper).

**Saída (processo não existe ou não está na fila)**
```json
{ "ok": false, "acao": "consulta.dados_contrato", "exit_code": 1,
  "erro": "processo 999999 nao existe" }
```
Nota: ao contrário da skill (que aceita `--forcar` para ensaiar processos fora da fila),
o wrapper devolve sempre os dados se o `id_processo` existir, esteja ou não
`contrato='Fazer' AND estado='Ativo'` — quem decide se pode gerar é o motor/a app
(`meta.na_fila`, mesmo campo que a skill já calcula), não o wrapper. Isto evita que o
wrapper tenha de saber a regra de negócio "na fila" e mantém-no simétrico a
`consulta.ficha` (que também devolve dados de processos fora da fila de Fichas).

**Exit codes**: 0 = sucesso; 1 = `id_processo` inexistente ou falha de SQL.

## Notas comuns às duas ações

- Nenhuma chave nova de SSH — usam a mesma chave de **consulta** já criada para Fichas
  (`docs/03-CONTRATO-WRAPPER-CT107.md` secção 1).
- `acoesPermitidas` do dispatcher (`docs/03-CONTRATO-WRAPPER-CT107.md` secção 9) passa a
  incluir `consulta.fila_contratos` e `consulta.dados_contrato`.
- Nenhuma password nem PII desnecessária em stderr; mesma regra do documento 03.
- O wrapper em si (implementação real em PHP 7.3 no CT 107) **fica para a Fase 1/3** —
  este documento fixa só o contrato (entrada/saída/exit codes), tal como o 03 fixou o de
  Fichas antes de existir código. Para a Fase 2 (motor, este commit), os dados de entrada
  do comando `contratos:gerar` são passados diretamente em JSON com esta mesma forma
  (`resultado` acima), sem passar pelo wrapper — ver `docs/09-CONTRATOS-CAMPOS-MAPA.md`.
