# Módulo — Caixa de Entrada Fiscal (consulta DF-e na SEFAZ)

**Nome no produto:** **Caixa de Entrada Fiscal** (menu e telas).  
**Nome técnico / rotas:** prefixo `dfe` ou `caixa-entrada-fiscal`.  
**Alternativas de mercado descartadas para o menu principal:** *Radar Fiscal* (marca, pouco autoexplicativo), *Monitor Fiscal* (Omie usa “Monitor de Notas”), *Entrada de Documentos* (genérico). *Caixa de Entrada* é o termo mais reconhecido em ERPs para PME (Bling, Tiny, etc.).

**Relacionados:** `Doc_Modulo_Financeiro.md` (NF operacional futura), `Doc_Modulo_Tenant.md`, `Doc_Jobs_e_Agendamento.md`, importação manual em `NfeImportadorController` / `NfeExtractorService`, importação XML NFS-e em venda via `NfseXmlImportService`.

**Status:** **Fase 1 em implementação** — DistDFe via `nfephp-org/sped-nfe` v5; certificado por empresa; job `dfe:sincronizar`. Cruzamento com compras/vendas: **Fase 2**.

---

## 1. Objetivo da Fase 1

Para cada **empresa** (`empresa`) com certificado A1 válido e **consulta ativa**:

1. Buscar na SEFAZ (e fontes NFSe aplicáveis) os documentos fiscais eletrônicos em que a empresa participa.
2. **Classificar** e listar por tipo de negócio (tabela abaixo).
3. Ao clicar em um documento: **resumo** (campos principais), **download do XML**, **impressão do DANFE/DACTE** (quando o layout existir para o modelo).
4. Executar a rotina de busca **1× por dia** por empresa habilitada (comando Artisan + `schedule`).

**Fora do escopo Fase 1:** gerar NF de compra/venda automaticamente, manifestação do destinatário em lote, escrituração, cruzamento com `nf` / estoque / parcelas.

---

## 2. Tipos de documento (classificação)

| Código interno | Rótulo na UI | Critério de classificação | Uso futuro (Fase 2) |
|----------------|--------------|---------------------------|---------------------|
| `NFE_RECEBIDA` | NF-e recebida | Modelo 55, CNPJ da empresa = **destinatário** | Compra, estoque, fornecedor, contas a pagar |
| `NFE_EMITIDA` | NF-e emitida | Modelo 55, CNPJ da empresa = **emitente** | Venda, faturamento, contas a receber |
| `CTE_TOMADO` | CT-e tomado | Modelo 57, empresa como **tomador** (ou destinatário conforme XML) | Frete, despesa, vínculo com NF-e |
| `NFSE_TOMADA` | NFS-e tomada | Prestador ≠ empresa; tomador = empresa | Serviços contratados / despesas |
| `NFSE_EMITIDA` | NFS-e emitida | Prestador = empresa | Receita de serviço |

**NFS-e nacional (ADN):** distribuição por NSU via `GET /contribuintes/DFe/{NSU}?cnpjConsulta=&lote=true` (mTLS, mesmo certificado A1). Ver § 8.

---

## 3. Cadastro da empresa — certificado A1

Bloco no formulário de empresa (`resources/views/admin/empresas/_form.blade.php`), seção **“SEFAZ — Certificado A1”** (conforme mock de UI):

| Campo | Tipo | Regras |
|-------|------|--------|
| Arquivo `.pfx` | upload | Obrigatório na primeira carga; opcional em update (“substituir certificado”). Armazenar fora do `public/`. |
| Senha do certificado | password | Criptografada (`encrypted` cast ou `Crypt::encryptString`); em update, vazio = manter senha atual. |
| Ambiente | enum | `homologacao` \| `producao` — define URLs dos webservices. |
| Consulta ativa | boolean | “Buscar na SEFAZ”; só empresas com flag `true` entram no job diário. |

**Banner de status (quando certificado válido):** nome do arquivo, validade (`notAfter` do X.509), ambiente, texto “senha armazenada de forma segura”.

**Validações ao salvar:**

- Extensão `.pfx` / `.p12`.
- Senha abre o PKCS#12.
- CNPJ do certificado = CNPJ da empresa (apenas dígitos).
- Certificado não expirado (avisar se &lt; 30 dias).

### 3.1 Modelo de dados sugerido

Tabela dedicada (evita colunas sensíveis em `empresa`):

**`empresa_certificado_sefaz`**

| Coluna | Tipo | Notas |
|--------|------|-------|
| `id` | PK | |
| `idempresa` | FK | unique por empresa (1 certificado ativo) |
| `idtenant` | FK | denormalizado para scope |
| `arquivo_path` | string | path no disk `local` ou `s3` privado |
| `arquivo_nome_original` | string | ex.: `certificado_20260519131725.pfx` |
| `senha_criptografada` | text | |
| `ambiente` | string(20) | homologacao/producao |
| `consulta_ativa` | boolean | default false |
| `validade_de` / `validade_ate` | datetime | extraído do certificado |
| `ultimo_nsu` | string(15) | NSU da última consulta DistDFe (por ambiente) |
| `ultima_consulta_em` | datetime nullable | |
| `ultima_consulta_status` | string nullable | ok, erro, parcial |
| `ultima_consulta_mensagem` | text nullable | |
| `timestamps` | | |

Model: `EmpresaCertificadoSefaz` com `belongsTo(Empresa)`.

---

## 4. Armazenamento dos documentos capturados

Tabela unificada para listagem e detalhe (independente de já existir linha em `nfes` / `nfses`):

**`dfe_documento`**

| Coluna | Tipo | Notas |
|--------|------|-------|
| `id` | PK | |
| `idempresa`, `idtenant` | FK | |
| `tipo` | enum | `NFE_RECEBIDA`, `NFE_EMITIDA`, `CTE_TOMADO`, `NFSE_TOMADA`, `NFSE_EMITIDA` |
| `modelo` | string(2) | 55, 57, etc. |
| `chave` | string(44) | unique com `idempresa` |
| `numero`, `serie` | string nullable | |
| `data_emissao` | datetime | |
| `valor_total` | decimal(15,2) nullable | |
| `cnpj_emitente`, `nome_emitente` | string | |
| `cnpj_destinatario`, `nome_destinatario` | string nullable | |
| `situacao_sefaz` | string nullable | autorizada, cancelada, etc. |
| `resumo_json` | json | campos para card/listagem (itens count, CFOP principal, etc.) |
| `xml_path` | string | XML completo (procNFe, procCTe, etc.) |
| `nsu` | string nullable | NSU DistDFe de origem |
| `idnfe` | FK nullable | vínculo opcional com `nfes.id` após parse |
| `idnfse` | FK nullable | vínculo opcional com `nfses.id` |
| `idnf` | FK nullable | **Fase 2** — NF operacional gerada/vinculada |
| `importado_em` | datetime | primeira captura |
| `timestamps` | | |

Índices: `(idempresa, tipo, data_emissao)`, `(idempresa, chave)` unique.

**Reutilização com código atual:**

- Após salvar XML, chamar lógica equivalente a `NfeExtractorService::extract()` para popular `resumo_json` e, se desejado, criar/atualizar `Nfe` + itens (sem criar `nf` na Fase 1).
- NFS-e: parser dedicado ou `NfseXmlImportService` se o XML for compatível.

---

## 5. Fluxo de consulta (SEFAZ)

```mermaid
sequenceDiagram
    participant Cron as schedule:run
    participant Cmd as dfe:sincronizar
    participant Job as SincronizarDfeEmpresaJob
    participant Svc as DfeDistribuicaoService
    participant SEFAZ as NFeDistribuicaoDFe
    participant DB as dfe_documento

    Cron->>Cmd: daily 03:00
    Cmd->>Job: dispatch por empresa (consulta_ativa)
    Job->>Svc: sincronizar(certificado, ultimo_nsu)
    loop até não haver mais doc
        Svc->>SEFAZ: nfeDistDFeInteresse(ultNSU)
        SEFAZ-->>Svc: lote resNFe / resEvento / ...
        Svc->>DB: upsert por chave + classificar tipo
    end
    Svc->>DB: atualizar ultimo_nsu, ultima_consulta_*
```

### 5.1 NF-e e CT-e (nacional)

- Webservice: **NFeDistribuicaoDFe** via `Tools::sefazDistDFe()` (`nfephp-org/sped-nfe` v5).
- **Ambiente:** a SEFAZ documenta DistDFe apenas em **produção** (`tpAmb=1`). No cadastro, use **Produção** no certificado; homologação fica bloqueada se `DFE_DISTDFE_APENAS_PRODUCAO=true` (padrão).
- **NF-e emitida:** a DistDFe **não** devolve notas em que a empresa é só emitente; essas exigem consulta emitente (futuro). A caixa preenche principalmente **NF-e recebida** (+ resumos até manifestação).
- Entrada: certificado A1 + CNPJ + UF (da empresa).
- Saída: XMLs compactados em `docZip`; descompactar, identificar schema (`procNFe`, `procCTe`, eventos).
- **NSU:** persistir `ultimo_nsu` por empresa/ambiente; na primeira execução usar `0` ou NSU informado.
- **Rate limit:** respeitar intervalo mínimo entre consultas (documentação SEFAZ); no job, uma empresa por vez ou fila com delay.

### 5.2 Classificação automática

Após parse do XML:

- NF-e: comparar CNPJ emitente/destinatário com CNPJ da `empresa`.
- CT-e: identificar papel da empresa (tomador, remetente, destinatário) via tags do CT-e; classificar `CTE_TOMADO` quando tomador = empresa.

Eventos (cancelamento, CC-e): atualizar `situacao_sefaz` no `dfe_documento` existente pela chave.

---

## 6. Interface (Fase 1)

### 6.1 Menu

**Fiscal → Caixa de Entrada Fiscal** (ícone caixa/inbox).

Permissão sugerida: `dfe.visualizar`, `dfe.certificado` (admin).

### 6.2 Listagem

Filtros: empresa (se multi-empresa no tenant), tipo (abas ou select), período emissão, busca por chave/CNPJ/nome.

Colunas: tipo (badge), número/série, emitente, destinatário, emissão, valor, situação.

Ação linha: **Abrir** → tela de detalhe.

### 6.3 Detalhe (ao clicar)

- Card **Resumo:** chave, modelo, emitente, destinatário, totais, natureza, transporte (se NFe), referência CT-e (se houver).
- Botões: **Download XML**, **Imprimir DANFE** (NFe modelo 55), **Imprimir DACTE** (CT-e) — geração via biblioteca sped (PDF) ou view HTML print-friendly na Fase 1.
- Lista resumida de itens (opcional, do `resumo_json`).

### 6.4 Empresa — certificado

Incluir partial `_certificado_sefaz.blade.php` no `_form` de empresas; POST separado ou mesmo `update` com validação isolada em `EmpresaCertificadoSefazController@store`.

---

## 7. Job e agendamento

| Item | Valor |
|------|--------|
| Comando | `php artisan dfe:sincronizar {--empresa=} {--debug}` |
| Comportamento | Lista `empresa_certificado_sefaz` com `consulta_ativa = 1` e certificado válido; dispara `SincronizarDfeEmpresaJob` por empresa (fila `database` ou `redis`). |
| Schedule | `dailyAt('03:00')` em `app/Console/Kernel.php` |
| Manual | Botão “Sincronizar agora” na listagem (apenas ADMIN, rate limit por empresa). |

Registrar em **`Doc_Jobs_e_Agendamento.md`** quando implementado.

**Requisito operacional:** `QUEUE_CONNECTION` ≠ `sync` em produção (mesmo padrão de `ProcessarPontuacaoCampanhaJob`).

---

## 8. NFS-e nacional (ADN) — implementado

| Componente | Descrição |
|------------|-----------|
| `DfeAdnNfseService` | Loop NSU em `contribuintes/DFe/{nsu}` (JSON `LoteDFe`, XML gzip+base64). |
| `AdnNfseHttpClient` | mTLS com PFX; produção `adn.nfse.gov.br`, homologação `adn.producaorestrita.nfse.gov.br`. |
| `DfeNfseClassificadorService` | `NFSE_TOMADA` / `NFSE_EMITIDA` por CNPJ tomador/prestador no XML nacional. |
| `ultimo_nsu_nfse` | NSU separado do NSU da NF-e (`ultimo_nsu`). |
| DANFSe | `GET /danfse/{chave}` — rota `admin.dfe.danfse`. |

**Pré-requisito:** município conveniado e NFS-e **compartilhada no ADN** (não é API municipal legada).

---

## 9. Segurança e compliance

- Certificado e senha **nunca** em log, resposta JSON ou backup público.
- Disk privado; permissão de leitura só no worker PHP.
- Rotacionar arquivo ao substituir; apagar PFX antigo do storage.
- Auditoria: `ultima_consulta_*` + log estruturado sem XML completo.
- Homologação obrigatória para testes antes de `producao`.

Variáveis `.env` sugeridas (implementação):

```env
DFE_SYNC_ENABLED=true
DFE_STORAGE_DISK=local
DFE_DAILY_AT=03:00
```

---

## 10. Arquivos a criar (checklist implementação)

| Camada | Arquivo / alteração |
|--------|---------------------|
| Migration | `empresa_certificado_sefaz`, `dfe_documento` |
| Models | `EmpresaCertificadoSefaz`, `DfeDocumento` |
| Services | `CertificadoA1Service`, `DfeDistribuicaoService`, `DfeClassificadorService`, `DfeResumoBuilder` |
| Jobs | `SincronizarDfeEmpresaJob` |
| Command | `DfeSincronizarCommand` |
| Controllers | `DfeDocumentoController`, `EmpresaCertificadoSefazController` (ou métodos em `EmpresaController`) |
| Views | `admin/dfe/index`, `admin/dfe/show`, partial certificado em empresas |
| Routes | `admin/dfe/*`, certificado em `empresas/{id}/certificado-sefaz` |
| Composer | `nfephp-org/sped-nfe` ^5.1 (**instalado**); `sped-cte` futuro para CT-e |
| Services | `SpedNfeConfigFactory`, `DfeDistribuicaoService` (loop DistDFe), `DfeClassificadorService`, `CertificadoA1Service` |
| Docs | `Doc_INDICE.md`, `Doc_Jobs_e_Agendamento.md`, trecho em `Doc_Inventario_Repositorio.md` |

---

## 11. Fase 2 (referência — não implementar agora)

- Botão **“Gerar compra”** / **“Vincular venda”** a partir de `dfe_documento` (reuso `NfeImportadorController` / fluxo compra).
- `ProdutoFornecedorMap` automático sugerido.
- Manifestação destinatário (ciência, confirmação).
- Dashboard: documentos sem NF operacional vinculada.

---

## 12. Critérios de aceite Fase 1

1. Admin cadastra PFX + senha + ambiente + liga “Buscar na SEFAZ” na empresa.
2. Job diário importa ao menos NF-e recebida/emitida em homologação (CNPJ de teste).
3. Listagem filtra por tipo; detalhe exibe resumo coerente com XML.
4. Download do XML e impressão DANFE funcionam para NF-e autorizada.
5. CT-e tomado aparece classificado quando retornado na DistDFe.
6. Documentos duplicados não duplicam linha (upsert por `chave` + `idempresa`).
7. Empresa com consulta desligada não é processada pelo job.

---

*Criado em 2026-05-19. Atualizar este documento ao concluir cada subentrega (certificado, DistDFe, UI, NFSe).*
