# Importação XLSX com mapeamentos

## Objetivo

O módulo `import_export` importa fichas de utentes a partir de ficheiros `.xlsx` sem depender da ordem ou do nome das colunas. Cada execução conserva o ficheiro privado, a configuração aplicada, o resultado por linha e um relatório Excel descarregável.

## Fluxo

1. Carregar o XLSX.
2. Selecionar a folha e a linha que contém os cabeçalhos.
3. Rever as sugestões automáticas.
4. Mapear cada coluna para um ou vários campos.
5. Definir regras de criação/atualização e o identificador principal.
6. Executar primeiro **Validar sem gravar**.
7. Corrigir o ficheiro ou o mapeamento e executar a importação real.
8. Descarregar o relatório XLSX.

## Destinos suportados

O catálogo inclui:

- dossiê e respetiva referência externa;
- utente, datas administrativas, morada, identificação e contactos;
- sala, código, valência e ano letivo;
- pai, mãe, encarregado de educação e titular do contrato;
- até quatro outros responsáveis;
- até quatro sócios.

Os campos que não existem como colunas próprias no esquema relacional são guardados no JSON `metadata`, mantendo-os disponíveis para PDF, comunicações e futuras extensões.

## Mapeamentos múltiplos

Uma coluna pode preencher vários destinos. Exemplo: `Nº Utente` pode preencher simultaneamente:

- `student.student_number`;
- `dossier.external_reference`.

Várias colunas também podem preencher o mesmo destino. Para campos textuais, os valores não vazios são concatenados pelo separador configurado. Exemplo: `PrimeiroNome` + `Apelido` → `student.name`.

## Conversões

- automática segundo o tipo do campo;
- texto sem alteração ou com trim;
- maiúsculas, minúsculas e capitalização;
- identificador, removendo o sufixo `.00` de números Excel;
- datas automáticas, `DD/MM/AAAA` ou `AAAA-MM-DD`;
- inteiro e decimal com vírgula portuguesa;
- booleano (`Sim`, `Não`, `1`, `0`, `true`, `false`);
- email em minúsculas;
- telefone;
- apenas algarismos para NIF/NISS.

## Identificador e duplicados

Por defeito, o identificador principal é o **Nº Utente**. Também pode ser utilizada a referência externa do dossiê.

A execução bloqueia ou ignora:

- identificadores repetidos no próprio ficheiro;
- criação de um identificador que já exista em modo “criar apenas”;
- atualização de um identificador inexistente em modo “atualizar apenas”;
- colisões entre uma ficha de utente e um dossiê incompatível.

Nunca são usados IDs internos importados de outro sistema para localizar fichas.

## Atualizações

`preserve` mantém o valor atual quando a célula está vazia. `clear` limpa o valor atual quando o mapeamento envia um vazio. Por segurança, `preserve` é o modo recomendado.

Nomes de utentes e responsáveis podem ser uniformizados em maiúsculas. Salas em falta podem ser criadas automaticamente ou substituídas por uma sala forçada escolhida no painel.

## Validação e relatório

A validação usa a mesma leitura, transformação e pesquisa de duplicados da importação real, mas não grava dossiês, utentes, salas ou responsáveis.

O relatório contém:

- resumo da execução;
- linha do ficheiro;
- estado e ação prevista/realizada;
- identificador;
- ID do registo associado;
- erros e avisos;
- valores mapeados;
- dados de origem.

Todos os valores textuais são escritos no relatório como texto explícito, impedindo que conteúdos iniciados por `=`, `+`, `-` ou `@` sejam executados como fórmulas ao abrir o resultado.

## Templates

Um template guarda:

- folha e linha do cabeçalho;
- todos os mapeamentos múltiplos;
- constantes;
- conversões;
- operação e identificador;
- política de vazios e inválidos;
- sala/ano letivo forçados;
- criação de salas e uniformização de nomes.

Templates podem ser exportados individualmente ou em conjunto para JSON e importados noutra instalação. Na aplicação de um template, as colunas são reconciliadas pelo cabeçalho quando as letras mudam.

## Segurança

- apenas `.xlsx` sem macros;
- armazenamento privado e checksum SHA-256;
- validação do contentor ZIP;
- limites de tamanho, expansão, folhas, linhas e colunas;
- bloqueio de caminhos internos inseguros;
- bloqueio configurável de ligações externas e fórmulas;
- leitura `data-only`, sem executar fórmulas;
- processamento em chunks numa fila dedicada;
- isolamento por `tenant_id` em templates, execuções, resultados e dados criados;
- cancelamento cooperativo entre chunks;
- uma tentativa automática por job, evitando reexecuções silenciosas após gravações parciais.

## Worker

```bash
php artisan queue:work --queue=imports,communications,signatures,pdf,default --tries=3 --timeout=1800
```

O job de importação define internamente uma tentativa única. Uma repetição é sempre iniciada explicitamente no painel, ficando registada na auditoria.

## Variáveis

```dotenv
IMPORT_QUEUE=imports
IMPORT_DISK=private
IMPORT_MAX_FILE_BYTES=52428800
IMPORT_MAX_UNCOMPRESSED_BYTES=536870912
IMPORT_MAX_ZIP_RATIO=150
IMPORT_MAX_SHEETS=30
IMPORT_MAX_ROWS=100000
IMPORT_MAX_COLUMNS=500
IMPORT_PREVIEW_ROWS=8
IMPORT_CHUNK_SIZE=500
IMPORT_REJECT_EXTERNAL_LINKS=true
IMPORT_REJECT_FORMULAS=false
```

## Limpeza de uploads abandonados

O scheduler executa diariamente:

```bash
php artisan imports:purge-abandoned
```

Por defeito remove uploads nunca executados com mais de 24 horas e execuções falhadas/canceladas com mais de 30 dias. Execuções concluídas e os respetivos relatórios não são eliminados automaticamente.
