# XML Monitor — SaaS de XMLs de compra

Sistema multiempresa que baixa automaticamente os XMLs das NF-e emitidas **contra**
o CNPJ de cada empresa cadastrada, usando o serviço oficial **NFeDistribuicaoDFe**
da SEFAZ Ambiente Nacional e o certificado digital A1 da própria empresa.

## Por que não o portal com reCAPTCHA

O `consultaRecaptcha.aspx` do portal da NF-e exige captcha por consulta e só devolve
uma nota por vez, a partir de uma chave de acesso **já conhecida** — não serve para
monitoramento automático, e automatizá-lo viola os termos de uso do portal.

O caminho legítimo, e o que os produtos do mercado usam, é o `NFeDistribuicaoDFe`:
a SEFAZ mantém uma fila numerada (NSU) de todos os documentos destinados a um CNPJ,
e o titular do certificado consulta essa fila dentro dos limites do serviço.
É exatamente o que este sistema faz.

## Stack

| Camada | Escolha | Motivo |
|---|---|---|
| Framework | Laravel 13 (PHP 8.3) | Roda em cPanel compartilhado sem extensões exóticas |
| Interface | Livewire 3 + Tailwind 4 | Painel reativo sem build de Node no servidor |
| SEFAZ | `nfephp-org/sped-nfe` 5.2 | Biblioteca de referência para SOAP + assinatura XML |
| Banco | MySQL / MariaDB | Padrão em hospedagem compartilhada |
| Fila e cache | driver `database` | Sem Redis nem supervisor em shared hosting |

## Como o monitor funciona

```
cron (1 min) → schedule:run
                 ├── xml:monitor                      (a cada 15 min)
                 │     └── SincronizarEmpresaJob      (por empresa elegível)
                 │           └── DistribuicaoDfeService::sync()
                 │                 ├── sefazDistDFe(ultNSU)   ← laço até maxNSU
                 │                 ├── gzdecode(base64(docZip))
                 │                 ├── DfeParser → resNFe | nfeProc | eventos
                 │                 └── XmlStorage → storage/app/xmls/{empresa}/{ano}/{mês}
                 └── queue:work --stop-when-empty     (a cada 5 min)
```

O cursor NSU é gravado **a cada lote**: se o processo cair no meio, a execução
seguinte retoma exatamente de onde parou, sem rebaixar documentos.

### Resumo x XML completo

A SEFAZ entrega `resNFe` (resumo) para notas ainda não manifestadas. O XML completo
(`nfeProc`) só é liberado após o evento **210210 — Ciência da Operação**. O sistema
suporta as quatro manifestações, sob demanda na tela da nota ou em lote quando a
empresa habilita `manifestacao_automatica`.

## Perfis de acesso

| Perfil | Alcance |
|---|---|
| **Master** | Todas as empresas, todos os XMLs, logs globais, personificação para suporte |
| **Administrador** | A própria empresa: certificado, monitor, usuários, manifestação |
| **Operador** | A própria empresa: consulta e download dos XMLs |

O isolamento é aplicado por um *global scope* (`App\Models\Scopes\CompanyScope`) em
`NotaFiscal`, `NotaEvento` e `SyncLog`, e está coberto por testes.

## Segurança dos certificados

- O `.pfx` vai para `storage/app/certificados/`, fora do document root, com nome aleatório.
- A senha é gravada com o cast `encrypted` do Eloquent (AES-256 com a `APP_KEY`).
- O CNPJ do titular é conferido contra o CNPJ da empresa no momento do upload.
- Os XMLs também ficam em disco privado: todo download passa por controller autorizado.

## Rodando localmente

```bash
composer install
npm install && npm run build
cp .env.example .env
php artisan xml:instalar        # chave, migrações e usuário master
php artisan serve
```

Testes:

```bash
php artisan test
```

## Comandos

```bash
php artisan xml:instalar                      # instalação guiada
php artisan xml:instalar --somente-master     # cria outro usuário master
php artisan xml:diagnostico                   # confere requisitos, permissões, banco e cron

php artisan xml:monitor                       # empresas elegíveis, via fila
php artisan xml:monitor --sync                # executa na hora, com saída no terminal
php artisan xml:monitor --force               # ignora o intervalo mínimo
php artisan xml:monitor --company=11222333000181
```

## Instalação em produção

Guia completo — requisitos, variáveis de ambiente, estrutura de diretórios, cron
e problemas comuns — em **[`INSTALACAO.md`](INSTALACAO.md)**.

Os arquivos prontos para o `public_html` ficam em [`deploy/`](deploy/).
