# 01 — Manifesto da ficha: explicação campo a campo

Companheiro de `01-MANIFESTO-SCHEMA.json`. O schema é a fonte de verdade de tipos/formatos; este documento explica origem e regra de normalização de cada campo. Compatibilidade exata com o que `inserir.php` valida e grava (ver `SKILL.md` e o próprio script, lidos para este documento).

## Estrutura de topo

| Chave | Obrigatória | Notas |
|---|---|---|
| `portal_submissao_id` | Não (default 0) | Vem de `portal_lookup.php`. 0 = sem submissão. |
| `readmissao` | Não | Só `true` quando o NIF já existe em `funcionarios` com `estado='Desativo'`; sem este campo, `inserir.php` aborta se vir NIF existente. |
| `funcionario` | Sim | Bloco principal. |
| `contrato_termo` | Não (`null` = não respondido) | Só se a modalidade do termo foi perguntada e respondida. |
| `condicoes` | Não (`null` = defaults) | Só se o utilizador pediu algo diferente dos defaults (tudo 0, dias 1-5). |

## Bloco `funcionario`

| Campo | Origem esperada | Normalização / regra |
|---|---|---|
| `nome` | Documento (CC) | NFC aplicado pelo script; não pré-normalizar acentos manualmente. |
| `nif` | Documento (CC) / lista do utilizador | 9 dígitos, checksum mod-11 validado na Fase A (não pelo `inserir.php`, que só valida a forma). |
| `admissao` | Reserva (`SELECT admissao FROM processo WHERE nif=? AND estado='Reservado'`) | **Nunca se pergunta ao utilizador.** |
| `mai` | Lista do utilizador / documento | 3 a 8 dígitos. |
| `ncc` | Documento (CC) | Só os 8 dígitos, sem dígito de controlo nem sufixo de versão (`13717747 0 ZW2` → `"13717747"`). Estrangeiro: nº do título de residência ou passaporte. |
| `niss` | Documento | Checksum específico (ver schema); falha de checksum não bloqueia sozinha, mas é "incerta" no relatório. |
| `filho_de` | Documento (CC) | Separador `" e "`, nunca `" * "`. |
| `nascimento`, `validade_cc` | Documento (CC) | ISO `YYYY-MM-DD`. |
| `especialidades` | Cartões PSP confirmados pelo utilizador na tabela de aprovação | Separadas por `/`. Nunca inferidas só pela presença do cartão na pasta — cartões de especialidades sem vínculo aparecem por vezes na raiz (ver `ocr-mini/RESULTADOS-2026-09-22.md`). |
| `val_vig`/`val_spr`/`val_ard`/`val_are`/`val_outro` | Cada cartão lido individualmente | Um `val_*` só se preenche se o cartão dessa categoria foi mesmo lido; cruzar pelo sufixo do nº (02x VIG, 04x SPR, 05x ARD, 06x ARE) contra a categoria impressa. |
| `morada` | Documento (CC/certidão AT > fatura/carta recente > portal) | Normalização obrigatória ao padrão da casa: `<Artéria> <Nome> nº<n>[ <compl>], <CP> <Localidade>`. Mostrar sempre `original → normalizada` e a fonte. |
| `iban` | Comprovativo de IBAN | Transcrever tal como impresso, contar os dígitos antes do mod-97 (só PT). Aceita qualquer IBAN válido ISO 13616. |
| `contacto`, `email` | Portal dos dados (prioridade) | Confiança alta automática quando vêm do portal (fonte digital direta). |
| `naturalidade`, `concelho`, `distrito`, `nacionalidade` | Documento (RC) | RC novo (pós 2026-09-01) só dá "Portugal" ou nada — não inventar. `distrito` deriva-se do `concelho` pelo helper `ocr_distrito_de_concelho`, nunca da linha COMARCA. |
| `val_rc` | Documento (RC) | Data de "CÓDIGO VIGENTE ATÉ", nunca a emissão; exige finalidade "exercício da atividade de segurança privada". |
| `estado_civil`, `ndependentes`, `habilitacoes` | Portal dos dados | Vazio se o candidato não respondeu; não inventar. |
| `zona1`, `zona2` | Lista do utilizador / lista de zonas ativas da BD alvo | `zona2` pode vir vazia/"Selecione..." (o script normaliza para vazio). |
| `local_servico`, `local_servico_ncontrato` | Lista do utilizador, cruzado com `clientes` | `local_servico_ncontrato` vazio = sem afetação de equipa (texto livre). |
| `nhoras`, `periodo_horas` | Lista do utilizador ou cálculo (dias × horas/dia) | `null` em `nhoras` = não respondido; nunca reutilizar um cálculo de outro cliente. |
| `data_consulta` | Utilizador/sistema | Data da consulta ao processo, se aplicável. |
| `notas` | — | **Deixar sempre vazio.** O script ignora e avisa se vier preenchido; nunca é este o sítio para anotações. |
| `mec` | Normalmente vazio | O script recupera automaticamente de um processo anterior, se existir. |
| `nome_emergencia`, `contacto_emergencia`, `relacao_emergencia` | Portal dos dados | — |
| `rh_status` | Pergunta ao utilizador (default "Não") | "Enviar" está fora do âmbito da v1 (email real de credenciais). |

## Bloco `contrato_termo` (opcional)

Só existe se a modalidade do termo foi respondida. Campos condicionais por modalidade (replicados do `addfuncback`):
- `sem_termo` → `data_fim`, `duracao_meses`, `motivo_termo` ficam vazios/null, `renovavel=0`.
- `termo_incerto` → `renovavel=0` sempre.
- `renovavel != "1"` → `renov_duracao`/`renov_previstas` ficam `null`.

`motivo_termo` obrigatório (com label completo) quando a modalidade não é `sem_termo` — pergunta que a skill trata como obrigatória sempre que a modalidade o exige.

## Bloco `condicoes` (opcional)

`null` = todos os defaults do `addfuncback` (0 em tudo, `dias_trabalho` = `1,2,3,4,5`). Só se preenche se o utilizador pedir algo diferente — nomeadamente os duodécimos de férias/Natal, perguntados sempre na caixa de condicionais.

## Campos que o script fixa e ignora (não pôr no manifesto, ou irrelevante se posto)

| Campo | Valor fixo pelo script |
|---|---|
| `contrato` | `'Fazer'` sempre (fila de contratos a redigir) |
| `estado` | `'Ativo'` |
| `demissao` | `''` |
| `n_adenda` | `0` |
| `notas` (processo) | `''` — mesmo que o manifesto traga texto, é ignorado com aviso em stderr |
| Condições salariais não incluídas em `condicoes` | Default 0 / dias `1,2,3,4,5` |

## Casos especiais

- **Re-admissão**: acrescentar `"readmissao": true` ao nível de topo. O script exige que o NIF já exista com `estado='Desativo'`; herda automaticamente `mec`, `naturalidade/concelho/distrito/nacionalidade` (se vazios) e, se o reuso de 24 meses for permitido, os 8 campos pessoais do processo anterior.
- **IBAN**: manifesto pode ir compacto ou com espaços; o script grava sempre agrupado de 4. Não validar formato próprio mais restritivo do que o schema (aceita qualquer IBAN ISO 13616 válido, não só PT).

Decisão tomada (Fase 2, decisão 8 do `07-DECISOES-FASE0.md`): o bloco `funcionario` usa sempre string vazia `""` para um campo não respondido (nunca omite a chave), exceto `nhoras` (`null` = não respondido). Os blocos `contrato_termo` e `condicoes`, acrescentados na Fase 4b (23/09/2026), seguem uma convenção diferente e deliberada: campos com tipo `integer` ou com `pattern`/`format` de data no schema (`duracao_meses`, `data_fim`, `renov_duracao`, `renov_previstas`, `valor_fundo_desemprego`, `valor_quota_sindical`) são **omitidos** quando não têm valor, porque uma string vazia nesses campos reprova a validação do schema — `inserir.php` trata "omitido" e "vazio" da mesma forma (`campo($arr,$chave,$default='')`), por isso a omissão é segura.

**Problema 1 (grave, descoberto 23/09/2026):** até esta correção, `GeradorManifesto::montar()` nunca incluía `contrato_termo` nem `condicoes` no manifesto — qualquer apply feito a partir da webapp inserRIA sem contrato de trabalho nem condições salariais no Sabichão, mesmo que o caso tivesse essa informação. Corrigido: os dois blocos são montados a partir dos campos estruturados do "Novo caso"/página do caso (`fichas_casos.contrato_*`/`cond_*`, migração `2026_09_23_000003`) e vão sempre no manifesto — `condicoes` nunca mais é `null` a partir desta versão (mesmo sem o utilizador mexer em nada, leva os defaults: tudo 0 e dias 1-5, exceto os dois duodécimos, marcados por omissão).

**Correção ao schema (23/09/2026):** vários campos opcionais do bloco `funcionario` (`niss`, `nascimento`, `validade_cc`, `val_vig/spr/ard/are/outro`, `val_rc`, `data_consulta`, `iban`, `email`) tinham `pattern`/`format` que só aceitava o formato preenchido, nunca a string vazia da decisão 8 — como `GeradorManifesto::validar()` nunca tinha sido chamado em produção (código morto), ninguém tinha reparado que o schema reprovava o próprio manifesto que o motor gera para praticamente qualquer caso real (documentos por ler = campos vazios). Os `pattern` destes campos passaram a aceitar `^$|<formato original>`, sem alterar `inserir.php` nem o comportamento de gravação (que já tratava vazio e ausente da mesma forma).
