# Módulo — Emissão de NFS-e (foco NFS-e Nacional / DPS)

**Nome no produto:** **Emissão de NFS-e**
**Nome técnico / rotas:** prefixo `nfse` (emissão), reaproveitando `nf` (venda de serviço) e `Caixa de Entrada Fiscal` (`dfe`) para armazenamento/consulta.
**Público:** prestadores de serviço, multiempresa/multitenant (`idtenant`).
**Certificado:** A1 (PFX/P12), reaproveitando `EmpresaCertificadoSefaz` + `CertificadoA1Service`.

**Status:** **Fases 1–4 + UI/filas implementadas** — caminho **NFS-e Nacional/DPS** como prioridade. Município piloto: **Uberlândia/MG (IBGE 3170206)**, aderente ao padrão nacional. Falta validar montagem/assinatura da DPS e contrato SEFIN contra a documentação oficial (ver §9) antes de produção.

**Relacionados:** `Doc_Modulo_Caixa_Entrada_Fiscal.md` (recepção DF-e/ADN), `Doc_Modulo_Produto_Servico.md`, `Doc_Modulo_Financeiro.md`, `Doc_Modulo_Tenant.md`.

> **Diretriz estratégica:** NFS-e Nacional/DPS é o caminho preferencial. Provedores municipais (Betha, Ginfes, ISSNet, SimplISS, prefeitura própria) são **fallback** para municípios não aderentes. Nenhuma regra fiscal municipal é hardcoded — tudo parametrizável por município/serviço/empresa.

---

## 1. Diagnóstico — o que já existe e é reaproveitável

| Componente | Papel atual | Uso na emissão |
|---|---|---|
| `EmpresaCertificadoSefaz` (`empresa_certificado_sefaz`) | Certificado A1 por empresa (storage privado, senha `Crypt`, ambiente, validade, NSU) | **Base do certificado para assinar a DPS** |
| `CertificadoA1Service` | Lê PFX (openssl + fallback CLI `-legacy`), extrai cert/chave PEM, valida CNPJ | **Crítico — assinatura XMLDSig da DPS** |
| `AdnNfseHttpClient` | Cliente mTLS GET (distribuição DFe + DANFSe) — base URL **ADN** | mTLS reaproveitado; **emissão usa SEFIN** (URL diferente) |
| `DfeAdnNfseService` / `DfeNfseClassificadorService` / `DfeStorageService` / `DfePdfArmazenamentoService` | Recebimento/armazenamento de XML/PDF NFS-e | Armazenar XML/PDF autorizados |
| `NfseXmlImportService` | Parser manual de XML NFS-e | Base do **provider Manual** |
| `DfeDocumento` (`dfe_documentos`) | Repositório unificado (`NFSE_EMITIDA`) | Destino do XML/PDF autorizado |
| `Nf` (`nf`) | Venda de serviço (`tipo_documento_fiscal='NFSE'`, scope `vendaServico`) | Documento de origem da NFS-e |
| `Nfse` (`nfses`) | Registro fino 1:1 com `Nf` | **Expandido** para ciclo de vida da emissão |
| `ProdutoServico` (`prodserv`) | `item_lista_servico`, `codigo_tributacao_nacional/municipal`, `codigo_nbs`, `descricao_nfse` | **+ tributação padrão** (ISS/retenções/IBS-CBS) |
| `CodigoTributacaoNacional` (global) | Código nacional + LC 116 | Reaproveitado |
| `BrasilApiService` | Consulta CNPJ/CEP (CNAE, Simples/MEI) | Preenchimento assistido |
| `SincronizarDfeEmpresaJob` + `dfe:sincronizar` | Padrão Job/Command/fila | Referência p/ jobs de emissão |

## 2. Lacunas (criadas nesta implementação)

- **Tabela `municipios`** (IBGE + adesão nacional + provider municipal + URLs/leiaute) — materializa a regra de decisão.
- **Campos fiscais em `empresa`**: inscrição municipal, código IBGE, regime tributário, optante Simples, incentivador cultural, natureza jurídica, CNAE, e-mail/telefone fiscal, série/numeração DPS.
- **Tributação em `prodserv`**: alíquota ISS, exigibilidade, retenção ISS, retenções federais (PIS/COFINS/CSLL/IRRF/INSS), IBS/CBS (parametrizável), município de incidência padrão.
- **Campos fiscais em `pessoa`** (tomador): tipo PF/PJ/exterior, IBGE, inscrição municipal, país, indicador de retenção ISS.
- **Expansão `nfses`**: modo/provider/ambiente, valores, base ISS, retenções, IBS/CBS, chave, número/série DPS, protocolo, XML enviado/autorizado, PDF, datas, usuário, status padronizado.
- **Tabela `nfse_emissao_logs`** (auditoria de cada tentativa, sem dados sensíveis).
- **Abstração de providers** (`NfseProviderInterface` + Nacional/Manual/Municipal) e serviços de emissão/validação/cálculo/DPS.

## 3. Arquitetura (adaptada ao padrão Services do projeto)

```
app/Services/Nfse/
  Contracts/NfseProviderInterface.php
  Dto/NfseEmissaoData.php  Dto/NfseResultado.php
  NfseProviderResolver.php        ← decide provider pelo município da empresa
  NfseEmissaoService.php          ← orquestra (sem regra fiscal em controller)
  Providers/NacionalNfseProvider.php   ← preferencial (SEFIN Nacional)
  Providers/ManualNfseProvider.php     ← reusa NfseXmlImportService
  SefinNfseHttpClient.php         ← POST mTLS de emissão (SEFIN, ≠ ADN)
  Dps/DpsBuilder.php              ← monta XML da DPS (leiaute nacional)
  Dps/DpsAssinaturaService.php    ← XMLDSig com CertificadoA1Service
  Calculo/NfseTributacaoCalculator.php ← ISS/retenções/IBS-CBS (parametrizável)
  Validators/NfseEmissaoValidator.php  ← pré-validação (envio) + validarPreCadastro (prévia/UI)
app/Enums/Nfse/{NfseStatus,ModoEmissao,RegimeTributario,IssExigibilidade}.php
app/Jobs/EmitirNfseJob.php             ← emissão assíncrona (fila), dispara DANFSe ao autorizar
app/Jobs/BaixarDanfseNfseJob.php       ← baixa PDF/DANFSe e grava nfses.pdf_path
app/Console/Commands/NfseAlertarCertificadosCommand.php ← nfse:alertar-certificados (agendado 06:00)
config/nfse.php                   ← URLs SEFIN/ADN, versões de leiaute, flags Reforma
```

Contrato comum: `emitir`, `consultar`, `cancelar`, `substituir`, `baixarPdf`. Resolver retorna **Nacional** por padrão quando `municipios.modo_emissao = NACIONAL`.

## 4. Fluxo de emissão Nacional/DPS

1. Venda de serviço (`nf`, `tipo_documento_fiscal=NFSE`) confirmada na tela assistida.
2. `NfseEmissaoService` resolve provider pelo município da empresa (Nacional p/ Uberlândia).
3. `NfseTributacaoCalculator` calcula ISS/retenções/IBS-CBS (parametrizado por serviço/empresa/município).
4. `NfseEmissaoValidator` pré-valida (certificado válido, CNPJ, IM, LC 116, valores, ambiente, numeração).
5. `DpsBuilder` monta o XML da DPS; `DpsAssinaturaService` assina (XMLDSig, certificado A1).
6. `nfses` → `ENVIADO` + XML DPS armazenado.
7. `SefinNfseHttpClient` faz `POST` (gzip+base64, mTLS) ao SEFIN Nacional (homologação/produção restrita primeiro).
8. Retorno: autorizado (chave, protocolo, XML NFS-e) ou rejeição → `nfses` `AUTORIZADO`/`REJEITADO` + log.
9. XML autorizado em `dfe_documentos` (`NFSE_EMITIDA`); DANFSe (PDF) baixado por job.

## 4.1. UI / cadastros (implementado)

**Campos fiscais nos formulários** (com persistência):

| Tela | Arquivo | Campos | Persistência |
|---|---|---|---|
| Empresa | `admin/empresas/_form.blade.php` | Inscrição municipal, IBGE, regime tributário (enum), regime especial, natureza jurídica, CNAE, e-mail/telefone fiscal, switches Simples/incentivador | `EmpresaController` (`$request->all()`/`except`, via `fillable`) |
| Serviço | `admin/produto-servico/_form.blade.php` (bloco "NFS-e nacional") | Alíquota ISS, exigibilidade (enum), município de incidência, ISS retido, retenções federais (PIS/COFINS/CSLL/IRRF/INSS), classificação/CST IBS-CBS | `ProdutoServicoController@store/update` (listas `only()` + `boolean()`) |
| Cliente/Tomador | `admin/pessoas/_form.blade.php` (só cliente/fornecedor) | Tipo de documento (PF/PJ/EXTERIOR), inscrição municipal, IBGE, país, observações fiscais, indicador ISS retido | `ClienteController@store/update` (`only()`); `PessoaController` via `$request->all()` |

**Emissão assistida na venda de serviço** (`admin/vendas/_form.blade.php` + `edit.blade.php`):

- Botão **"Ver prévia / emitir"** no painel NFS-e (mostra selo de status quando já emitida).
- Endpoint **`GET admin/vendas/{venda}/nfse/previa`** → `NfseEmissaoService::previa()`: calcula valores/retenções e indica caminho (Nacional/Manual), município e ambiente, **sem reservar número nem enviar** (usa `NfseEmissaoValidator::validarPreCadastro()`).
- Modal de confirmação com tabela de valores e lista de pendências; botão **"Emitir"** só habilita se o pré-cadastro estiver OK e submete para `POST admin/vendas/{venda}/nfse/emitir`.
- Emissão pela UI é **síncrona** (feedback imediato); `EmitirNfseJob` fica disponível para recorrência/contratos e dispara `BaixarDanfseNfseJob` ao autorizar.

Rotas: `admin.vendas.nfse.previa` (GET), `admin.vendas.nfse.emitir` (POST), `admin.vendas.nfse.importar-xml` (POST).

**Ferramentas de diagnóstico (CLI):**

- `php artisan nfse:testar-emissao {idnf}` — **dry-run**: carrega contexto, valida pré-cadastro, monta o DTO, monta a DPS (com validação XSD se ligada), assina (SHA-256) e mostra Id/estrutura — **sem reservar número, sem enviar e sem persistir**. Flags: `--mostrar-xml` (imprime o XML assinado), `--enviar` (executa a emissão real e persiste).
- `php artisan nfse:alertar-certificados` — certificados A1 vencidos/a vencer (agendado 06:00).

## 5. Regras parametrizáveis (nunca hardcoded)

- Alíquota ISS / exigibilidade / retenção: `prodserv` + `municipios`.
- Local de incidência: `prodserv.codigo_municipio_incidencia_padrao` / `municipios`.
- Regime tributário: `empresa.regime_tributario`.
- Retenções federais: `prodserv` + regra por tomador PJ (**validar com contador**).
- Reforma (IBS/CBS): `config/nfse.php` + campos opcionais (período de teste a partir de 2026).

## 6. Reforma Tributária (IBS/CBS)

Campos `valor_cbs`/`valor_ibs` e classificação tributária **opcionais** agora, ligados por flag de leiaute (`config/nfse.php`). Sem alíquotas hardcoded. Convivência ISS × IBS/CBS na transição.

## 7. Segurança / compliance

- Certificado/senha nunca em log ou JSON; storage privado.
- Logs de emissão sem payload sensível.
- Isolamento por tenant (`TenantScope`); XML/PDF não vazam entre empresas.
- Homologação/produção restrita obrigatória antes de produção.

## 8. Backlog por fases

1. **Fase 1** — `municipios`, campos fiscais, `config/nfse.php`, Enums. ✅ *(concluída + UI/formulários)*
2. **Fase 2** — certificado A1 (reuso + alertas de expiração). ✅ *(reuso + `nfse:alertar-certificados` agendado)*
3. **Fase 3** — emissão Nacional/DPS em homologação. 🟡 *(assinatura SHA-256, endpoint SEFIN, contrato JSON e estrutura da DPS alinhados ao oficial — §9; falta rodar em produção restrita + validar XSD/códigos do ANEXO_I)*
4. **Fase 4** — armazenamento XML/PDF + consulta. 🟡 *(XML em `nfses`/`dfe_documentos`; DANFSe via `BaixarDanfseNfseJob`)*
5. **Fase 5** — cancelamento/substituição. ⬜ *(contrato definido na interface; implementar)*
6. **Fase 6** — providers municipais (fallback). ⬜
7. **Fase 7** — IBS/CBS completo. ⬜ *(campos/flags prontos; aguarda leiaute)*
8. **Fase 8** — filas/monitoramento/alertas. 🟡 *(`EmitirNfseJob`/`BaixarDanfseNfseJob` + alerta de certificado; falta dashboard de monitoramento)*

## 9. Validação contra a documentação oficial

### ✅ Confirmado e já aplicado no código (gov.br/nfse — Manual SN NFS-e + Anexos v1.01)

- **Assinatura XMLDSig**: Enveloped, **RSA-SHA256** (`xmldsig-more#rsa-sha256`), digest **SHA-256** (`xmlenc#sha256`), C14N (`REC-xml-c14n-20010315`), **EndCertOnly**. `Signature` é **irmã** de `infDPS` dentro de `DPS` (ação "after"). → `DpsAssinaturaService` atualizado de SHA-1 para SHA-256.
- **Endpoint SEFIN** (síncrono, mTLS A1): `POST {base}/SefinNacional/nfse`, body `{ "dpsXmlGZipB64": base64(gzip(xml DPS assinado)) }`; sucesso **201** → `{ tipoAmbiente, idDps, chaveAcesso, nfseXmlGZipB64, alertas }`; erro → `{ erros: [{ Codigo, Descricao, Complemento }] }`. Hosts: `sefin.producaorestrita.nfse.gov.br` (homolog) / `sefin.nfse.gov.br` (prod). → `config/nfse.php` + `SefinNfseHttpClient` alinhados.
- **chaveAcesso** = 50 posições, **retornada pelo SEFIN** (não derivada do Id da DPS). → provider usa `chaveAcesso` da resposta.
- **Id da DPS** (`TSIdDPS`): 45 chars = `DPS` + cMun(7) + tpInsc(1) + nInsc(14) + serie(5) + nDPS(15). `Id` só em `infDPS` (nunca na raiz `DPS`). **tpInsc: 1=CPF, 2=CNPJ** (≠ NF-e). → confirmado no `DpsBuilder` (gera Id de 45 chars).
- **regTrib**: ordem `opSimpNac → regApTribSN? → regEspTrib`; `opSimpNac` 1=Não/2=MEI/3=ME-EPP; `regApTribSN` exigido p/ Simples; `regEspTrib` **obrigatório**. → corrigido.
- **`tribMun`** (ordem `TCTribMunicipal` confirmada no XSD): `tribISSQN → [cPaisResult, tpImunidade, exigSusp, BM] → tpRetISSQN → pAliq?`.
  - **`tribISSQN`**: 1=Operação tributável; 2=Imunidade; 3=Exportação de serviço; 4=Não incidência.
  - **`tpRetISSQN`**: 1=Não Retido; 2=Retido pelo Tomador; 3=Retido pelo Intermediário. → **inversão corrigida** (antes `issRetido?'1':'2'`, agora `'2':'1'`).
  - **`pAliq`** é opcional (só quando o município de incidência **não** é parametrizado no SN); emitido quando há alíquota informada e **depois** de `tpRetISSQN`.
- **`totTrib`** (Lei da Transparência) é **obrigatório** em `trib` — choice `vTotTrib | pTotTrib | indTotTrib | pTotTribSN`. ME/EPP (opSimpNac=3) usa **`pTotTribSN`**; demais usam **`indTotTrib=0`** (não informar estimativa, Decreto 8.264/2014). → corrigido (sempre emite `totTrib`).
- **Informações complementares**: `serv/infoCompl/xInfComp` (confirmado no XSD; **não** existe `infComplem` em `infDPS`). → corrigido.
- **Valores** só em `<valores>` (sem `vBC/vISSQN/vLiq`); `vServ` como **string** 2 casas (`TSDec15V2`). → ok.
- **Versão de leiaute**: **v1.01** (09/02/2026). → `nfse.dps.versao = 1.01`.
- **Validação XSD local**: habilitável por `NFSE_DPS_VALIDAR_SCHEMA=true` + `NFSE_DPS_XSD_PATH` (pacote `NFSe-ESQUEMAS_XSD-v1.01` extraído em `storage/app/nfse/xsd`; é o **default** de `nfse.dps.xsd_path`). O `DpsBuilder` roda `schemaValidate` e lança os erros antes do envio (equivalente ao `RNG6110`). **Estrutura validada com sucesso contra o `DPS_v1.01.xsd` oficial** (Simples e Lucro Presumido).
- **Falso-positivo libxml (`serie`)**: o XSD declara `TSSerieDPS` com padrão `^0{0,4}\d{1,5}$`; o validador .NET do SEFIN trata `^`/`$` como âncoras, mas o libxml os interpreta como literais e rejeita qualquer série. O `DpsBuilder` **ignora** esse erro específico (série já é validada como dígitos no fluxo).

### ⚠️ A confirmar no ANEXO_I (planilha de leiaute) / homologação + contador

- **`cNBS`** vs **`cTribNac`**: `cNBS` é numérico (NBS); `cTribNac` é o código nacional (≠ item LC 116). → `cNBS` agora vem de `prodserv.codigo_nbs`.
- **Grupo IBS/CBS** (caminho `NFSe/infNFSe/DPS/infDPS/`, NT 004-RTC) — campos/indicadores do ANEXO_C; ainda **suspensos** na transição.
- **Retenções federais** consolidadas em campo único (NT 007/2026) — mapear no leiaute final.
- **Eventos** de cancelamento/substituição: `POST /SefinNacional/nfse/{chave}/eventos` com `pedidoRegistroEventoXmlGZipB64` (evento `e101101`, Id `PRE`+chave(50)+tpEvento(3)+nSeq(3)).
- **`pTotTribSN`** (alíquota efetiva do Simples) — deve vir de cadastro/contador (parametrizável; não emitido quando ausente).

---

*Criado em 2026-06-23. Atualizado em 2026-06-23 (UI/formulários fiscais, prévia de emissão, jobs assíncronos e alerta de certificado; assinatura SHA-256, contrato SEFIN e estrutura da DPS validados contra a doc oficial v1.01; **DPS gerada validada com sucesso contra o `DPS_v1.01.xsd` oficial** e correções aplicadas no `DpsBuilder`: ordem de `tribMun`, `tpRetISSQN`, `totTrib` obrigatório, `infoCompl/xInfComp`). Atualizar a cada subentrega (Fase 1 → 8).*
