# SPED Fiscal (EFD ICMS/IPI) — Estrutura e Mapeamento

Visão geral da implementação atual, cobrindo blocos 0, C, E, 1 e 9 e infraestrutura para evolução.

Versão de leiaute: ajustável via `cod_ver` na geração (`default: 017`).

Bloco 0 — Abertura e Cadastros
- `0000`: abertura do arquivo (período, finalidade, perfil, identificação da empresa, UF, IE/IM).
- `0001`: abertura do bloco 0 (indicador de movimento: `0` com movimento).
- `0005`: dados complementares da entidade (nome fantasia, endereço, contato, CEP).
- `0190`: unidades de medida utilizadas (derivadas de `products.unidade`).
- `0200`: itens do cadastro de produtos (código, descrição, EAN, unidade, tipo, NCM).
- `0150`: participantes (clientes e fornecedores); origem: tabelas `clients`, `suppliers` e `fornecedores`.

Mapeamentos de dados
- Empresa: primeira linha de `empresas` (campos básicos + endereço do módulo SettingsUI).
- Produtos: tabela `products` (`codigo`, `descricao`, `ean`, `unidade`, `ncm`).
- Unidades: `DISTINCT products.unidade`.
- Participantes: `clients` (CPF/CNPJ/IE) e `suppliers`/`fornecedores` (CNPJ/IE).

Bloco 9 — Controle e Encerramento
- `9001`: abertura do bloco 9.
- `9900`: ocorrência por registro (contagem de todas as linhas por `REG`).
- `9990`: encerramento do bloco 9 (`qtd_lin_9`).
- `9999`: encerramento do arquivo (`qtd_lin` total).

Validações iniciais
- CNPJ (14 dígitos), CPF (11 dígitos), UF (`[A-Z]{2}`), CEP (formato numérico).
- Presença de `empresas` com CNPJ e UF válidos para geração.

Evolução planejada
- Enriquecimento dos campos fiscais no Bloco C (CFOP, CST, bases/alíquotas) e consolidações (ex.: C190).
- Apuração real no Bloco E (E110/E111) a partir de `tax_data`, regras por NCM/UF e operações.
- Implementação dos blocos H e K (inventário/produção/estoques) conforme disponibilidade de dados.
- Validadores por registro e cruzamentos de consistência, com relatório para PVA.

API
- `POST /api/fiscal/sped/generate` (permite `X-API-Key`): body `{ year, month, perfil, ind_ativ, cod_fin, cod_ver, icms_rate?, pis_rate?, cofins_rate?, ipi_rate?, icms_st_rate?, e111?, d100_docs?, k230?, k235? }`; retorna `{ lines, txt }`. RBAC: `fiscal.sped.read`.
- `POST /api/fiscal/sped/validate` (permite `X-API-Key`): validação leve dos dados da companhia (`empresas`), checando `CNPJ` e `UF`. Retorna `{ ok, issues }`. RBAC: `fiscal.sped.read`.
- `POST /api/fiscal/sped/check` (permite `X-API-Key`): gera SPED com o mesmo corpo da geração e executa `SpedValidator::validateSpedLines`, retornando `{ ok, issues }`. RBAC: `fiscal.sped.read`.
 - `POST /api/fiscal/sped/auto` (somente autenticado): gera e salva o TXT em `storage/sped/<tenant>/<ano-mes>/`. RBAC: `fiscal.sped.manage`.

 Exportação
- Front-end em `/fiscal/sped` para prévia e download do TXT.
  - Configuração de taxas opcionais (`ICMS/PIS/COFINS/IPI/ICMS ST`) e envio de ajustes `E111` via JSON.
  - Seleção de versão de leiaute (`cod_ver`: 017, 016, 015) enviada aos endpoints.
  - Botão "Validar dados" que chama o endpoint leve de validação (CNPJ/UF).
  - Botão "Validar SPED gerado" que chama o endpoint de checagem e apresenta o relatório de issues.

Validações leves (dados da empresa)
- Campos verificados em `/api/fiscal/sped/validate`:
  - `CNPJ` (formato): deve conter 14 dígitos.
  - `UF` (sigla): dois caracteres `A-Z`.
  - `IE` e `IM`: devem estar informados.
  - Endereço: `logradouro`, `numero`, `bairro`, `cidade` não podem ser vazios; `CEP` deve conter 8 dígitos.

Blocos adicionais implementados

Bloco C — Documentos fiscais (Entradas/Saídas)
- `C001`: abertura do bloco (indicador de movimento `0`).
- `C100`: NF-e de saídas (`sales_orders`) e entradas (`purchase_invoices`), com campos essenciais (`IND_OPER`, `IND_EMIT`, `COD_MOD`, `COD_SIT`, `NUM_DOC`, `DT_DOC`, `DT_E_S`, `VL_DOC`, `VL_MERC`).
- `C170`: itens por documento, originados de `sales_order_items` e `purchase_invoice_items` (código/descrição/unidade/quantidade/valor), com `CST_ICMS` padrão (`000`) e `CFOP` padrão (`5102` para saídas, `1102` para entradas). Campos fiscais preenchidos: `VL_BC_ICMS = VL_ITEM`, `ALIQ_ICMS` (percentual), `VL_ICMS`; `VL_BC_ICMS_ST`/`ALIQ_ST`/`VL_ICMS_ST` (quando informado); `CST_IPI` (default `50`), `COD_ENQ` (default `999`), `VL_BC_IPI = VL_ITEM`, `ALIQ_IPI` (percentual), `VL_IPI`; `CST_PIS/COFINS = 01`, `VL_BC_PIS/COFINS = VL_ITEM`, `ALIQ_PIS/COFINS` (percentual), `VL_PIS/COFINS`. Taxas padrão: ICMS `0.18`, PIS `0.0165`, COFINS `0.076`, IPI `0.10`, ICMS ST `0.00` (configuráveis via `TaxConfig.options` ou parâmetros de geração). Regras por NCM/UF/operação podem sobrescrever as taxas via `TaxConfig.options.tax_rules`.
- `C190`: consolidação por documento agrupando operações por `CST_ICMS`/`CFOP`, preenchendo `ALIQ_ICMS` (percentual), `VL_OPR`, `VL_BC_ICMS`, `VL_ICMS`, `VL_BC_ICMS_ST`/`VL_ICMS_ST` e `VL_IPI` agregados.
- `C990`: encerramento do bloco C.

Bloco E — Apuração ICMS/IPI
- `E001`: abertura do bloco.
- `E100`: período de apuração (`DT_INI`/`DT_FIN`).
- `E110`: apuração básica calculada: `VL_TOT_DEBITOS = total_saídas * taxa`, `VL_TOT_CREDITOS = total_entradas * taxa`, `VL_SLD_APURADO = debitos - creditos`; `VL_ICMS_RECOLHER` (se saldo ≥ 0) ou `VL_SLD_CREDOR_TRANSPORTAR` (se saldo < 0). Taxa padrão `0.18` (configurável via `options.icms_rate` na geração ou `TaxConfig.options.icms_default_rate`).
- `E111`: ajustes opcionais da apuração (débitos/créditos) quando fornecidos via geração (`options.e111`) ou `TaxConfig.options.e111_entries`.
- `E990`: encerramento com contagem.

Validações
- Validador de linhas (`SpedValidator::validateSpedLines`) verifica:
  - Presença de aberturas/encerramentos essenciais: `C001/C990`, `E001/E990`, `1001/1990`, e trailer final `9999`.
  - Consistência interna do `E110`: fórmula do saldo apurado, coerência entre `VL_SLD_APURADO` e campos de recolhimento/credor transportar.
  - Consistência de `C190`: `VL_ICMS` compatível com `VL_BC_ICMS` e `ALIQ_ICMS` (dentro de tolerância de arredondamento). Mensagens utilizadas:
    - `C190 VL_ICMS superior ao esperado pela alíquota` quando o `VL_ICMS` excede o valor calculado por `base * alíquota`.
    - `C190 VL_ICMS inferior ao esperado pela alíquota` quando o `VL_ICMS` fica abaixo do valor calculado por `base * alíquota` além da tolerância.
    - `C190 valores negativos não permitidos` quando `VL_BC_ICMS` ou `VL_ICMS` são negativos.
  - Consistência de `D190`: `VL_ICMS` compatível com `VL_BC_ICMS` e `ALIQ_ICMS` (dentro de tolerância de arredondamento). Quando `ALIQ_ICMS` não for fornecida no payload `D100/D190`, ela é derivada automaticamente de `VL_BC_ICMS` e `VL_ICMS`. Regras e mensagens:
    - Formato: `CFOP` deve conter 4 dígitos numéricos (`D190 CFOP inválido (deve conter 4 dígitos)`); `CST` deve conter 2 dígitos (`D190 CST inválido (deve conter 2 dígitos)`).
    - Consistência de ICMS: `D190 VL_ICMS superior ao esperado pela alíquota` quando `VL_ICMS` excede o esperado por `base * alíquota`.
    - Consistência de ICMS: `D190 VL_ICMS inferior ao esperado pela alíquota` quando `VL_ICMS` fica abaixo do esperado por `base * alíquota` além da tolerância.
    - Valores: `D190 valores negativos não permitidos` quando base ou imposto são negativos.
    - Compatibilidade de isenção/suspensão: para `CST` `40`, `41` e `50`, base e ICMS devem ser zero (`D190 CST isento/suspenso exige base/ICMS zero`).
  - Consistência de `C170` IPI: `VL_IPI` compatível com `VL_BC_IPI` e `ALIQ_IPI`.
  - Consistência de `C170` PIS/COFINS: validação de arredondamento com tolerância pequena (`0.02`) para:
    - `C170 VL_PIS não confere com base/alíquota` quando `VL_PIS` diverge de `VL_BC_PIS * ALIQ_PIS` além da tolerância.
    - `C170 VL_COFINS não confere com base/alíquota` quando `VL_COFINS` diverge de `VL_BC_COFINS * ALIQ_COFINS` além da tolerância.
  - Cruzamento por documento: soma de `VL_ITEM` dos itens `C170` por `(CST, CFOP)` deve bater com `C190 VL_OPR` do documento correspondente. Mensagem: `C190 VL_OPR não confere com soma de VL_ITEM do documento (CST/CFOP)`.
  - Cruzamento por documento: `C190.VL_OPR` por `(CST,CFOP)` confere com a soma de `C170.VL_ITEM` do documento.
  - Reporta lista de issues (vazia quando tudo ok).

Bloco 1 — Outras informações
- `1001`: abertura sem movimento (`1`).
- `1990`: encerramento com contagem de linhas.

Bloco H — Inventário
- `H001`: abertura do bloco H (indicador de movimento `0`).
- `H990`: encerramento do bloco H.

Bloco K — Controle de produção e estoque
- `K001`: abertura do bloco K (indicador de movimento `0`).
- `K200`: saldo de estoque escriturado por item na data de encerramento.
- `K230`/`K235`: geração condicional via payload opcional; quando presentados, gera registros de produção (`K230`) e consumo (`K235`).
- `K990`: encerramento do bloco K.

Bloco D — Serviços de transporte
- `D001`: abertura do bloco (indicador de movimento `1` por padrão).
- `D100`/`D190`: geração condicional via payload opcional; quando presentados, gera documentos de transporte (`D100`) e consolidação (`D190`).
- `D990`: encerramento do bloco D.

Payload opcional (Blocos D e K)
- Objetivo: permitir testes e integrações progressivas fornecendo dados diretamente na geração.
- Campos no body da geração/checagem:
  - `d100_docs`: array de documentos de transporte para `D100`; cada item pode conter chaves como `cod_part`, `mod`, `ser`, `num_doc`, `dt_doc`, `dt_a_p`, `vl_doc`, `vl_serv`, `vl_bc_icms`, `vl_icms`, `cfop`, `cst`, `aliq_icms`, `tipo_doc`.
  - `k230`: array de produções com `dt_ini`, `dt_fin`, `cod_item`, `qtd`, `cod_doc`.
  - `k235`: array de consumos com `cod_item`, `qtd`, `cod_ins_subst?`.
  
  Exemplos (D100/D190):
  - Derivação de alíquota no `D190` quando omitida:
    ```json
    {
      "d100_docs": [
        {
          "ind_oper": 1,
          "ind_emit": 0,
          "cod_mod": "57",
          "cod_sit": "00",
          "serie": "1",
          "num_doc": "CTE-TEST-001",
          "dt_doc": "2025-11-01",
          "dt_apur": "2025-11-01",
          "vl_doc": 1000.00,
          "itens": [
            { "cst": "00", "cfop": "5353", "vl_opr": 1000.00, "vl_bc_icms": 1000.00, "vl_icms": 120.00 }
          ]
        }
      ]
    }
    ```
  - Inconsistência proposital para teste de validação (`VL_ICMS` superior ao esperado pela alíquota declarada):
    ```json
    {
      "d100_docs": [
        {
          "ind_oper": 1,
          "ind_emit": 0,
          "cod_mod": "57",
          "cod_sit": "00",
          "serie": "1",
          "num_doc": "CTE-INVALID-001",
          "dt_doc": "2025-11-01",
          "dt_apur": "2025-11-01",
          "vl_doc": 1000.00,
          "itens": [
            { "cst": "00", "cfop": "5353", "aliq_icms": 10.00, "vl_opr": 1000.00, "vl_bc_icms": 1000.00, "vl_icms": 150.00 }
          ]
        }
      ]
    }
    ```
- Notas:
  - Datas aceitam formatos `YYYY-MM-DD`, `DD/MM/YYYY` ou timestamp; são normalizadas internamente.
  - Valores são considerados numéricos em ponto flutuante; validações rejeitam negativos em registros consolidados (ex.: `D190`).
- Exemplo mínimo `d100_docs`:
  ```json
  [
    {
      "cod_part": "FORN001",
      "mod": "57",
      "ser": "1",
      "num_doc": "12345",
      "dt_doc": "2025-10-15",
      "vl_doc": 1000.00,
      "vl_bc_icms": 1000.00,
      "aliq_icms": 12,
      "vl_icms": 120.00,
      "cfop": "5353",
      "cst": "000"
    }
  ]
  ```
- Exemplo mínimo `k230` e `k235`:
  ```json
  {
    "k230": [
      { "dt_ini": "2025-10-01", "dt_fin": "2025-10-01", "cod_item": "PROD001", "qtd": 100 }
    ],
    "k235": [
      { "cod_item": "INSUMO001", "qtd": 50 }
    ]
  }
  ```