Fiscal

Como automatizar o SPED/EFD no Protheus

Apuração fiscal é processo crítico. Automação bem feita reduz horas de trabalho e elimina retrabalho por erro humano.

A apuração do SPED/EFD dentro do Protheus normalmente exige rodar vários processos, conferir validações e ajustar divergências. Cada passo manual é uma chance de erro — e de multa. Este artigo mostra o fluxo completo, as funções nativas do Protheus e como montar uma automação que realmente funciona.

Fluxo completo de apuração SPED

A ordem dos processos é crítica. Pular etapas ou invertê-las gera divergências que só aparecem na validação. O fluxo correto:

Fluxo de Apuração SPED/EFD
-- Etapa 1: Fechamento de estoque e custo
MATA220()  -- Fechamento mensal de estoque (apuração fiscal depende do custo)

-- Etapa 2: Apuração dos tributos
MATA953()  -- Apuração de ICMS (rotina nativa da Central TOTVS)
-- Apuração de IPI: rotina conforme release — confirme no menu Fiscal

-- Etapa 3: Geração e validação do arquivo EFD
SPDFIS()   -- Geração/validação do SPED Fiscal (EFD ICMS/IPI)

-- Etapa 4: Validação (cross-check)
-- Rotina customizada de validação (ver abaixo)

-- Etapa 5: Transmissão — NÃO é feita pelo Protheus nem pelo TSS
-- PVA da Receita: importar → validar → assinar (certificado A1/A3)
-- → transmitir via Receitanet

Cuidado com telas em automação: rotinas como FINA070/FINA050 (baixas) e MATA220 são telas para operação humana. Automação de back-end usa ExecAuto (MSCM260, FINA040 etc.), Schedule/Job aprovado e fluxo controlado — chamar tela em rotina automática quebra em ambiente sem usuário.

E o TSS? O TSS (TOTVS Sped Service) transmite DF-e (NF-e, CT-e, MDF-e). A transmissão do EFD ICMS/IPI passa pelo PVA da Receita + Receitanet — confundir os dois leva a projeto de integração errado desde o início.

Rotinas nativas por etapa

Antes de criar customizações, saiba o que o Protheus já oferece nativamente:

RotinaMóduloO que faz
MATA953()FiscalApuração do ICMS próprio e diferencial
SPDFIS()FiscalGeração/validação do SPED Fiscal (EFD ICMS/IPI)
MATA220()EstoqueFechamento mensal de estoque
PVA (Receita)ExternoValidação, assinatura e transmissão do EFD (via Receitanet)

Nota: rotinas de apuração de IPI e de EFD-Contribuições (PIS/COFINS) variam conforme release — valide no menu Fiscal do seu ambiente antes de citar em projeto. Não presuma função por nome de memória: confirme no ambiente.

Queries de validação antes da transmissão

A automação deve validar antes de transmitir. As queries abaixo estão em SQL puro (SSMS), com tabelas físicas (SF3010) e filial explícita — a regra é: filial + D_E_L_E_T_ em todo WHERE. E atenção: campos tipo data no Protheus são CHAR(8) em formato AAAAMMDD; "data vazia" é SPACE(8) (8 espaços), nunca um espaço só.

Validações SPED — Queries de Cross-Check (SQL puro)
-- 1. Notas canceladas que ainda aparecem no SPED
SELECT F3_FILIAL, F3_SERIE, F3_NFISCAL, F3_CLIEFOR, F3_DTCANC
FROM SF3010 SF3
WHERE SF3.F3_FILIAL = '01'
  AND SF3.F3_DTCANC <> '        '  -- vazio = 8 espaços (CHAR(8))
  AND SF3.F3_SERIE IN ('1', '2')
  AND SF3.F3_EMISSAO BETWEEN '20260801' AND '20260831'
  AND SF3.D_E_L_E_T_ = ' '

-- 2. CFOPs inconsistentes com a operação — join completo com loja
SELECT C5.C5_NUM, C5.C5_LOJACLI, C6.C6_PRODUTO, C6.C6_CF, C5.C5_TIPO
FROM SC5010 C5
JOIN SC6010 C6
  ON C6.C6_FILIAL = C5.C5_FILIAL
  AND C6.C6_NUM = C5.C5_NUM
WHERE C5.C5_FILIAL = '01'
  AND C5.C5_TIPO = 'N'
  AND C6.C6_CF LIKE '5%'
  AND C5.C5_EMISSAO BETWEEN '20260801' AND '20260831'
  AND C5.D_E_L_E_T_ = ' '
  AND C6.D_E_L_E_T_ = ' '

-- 3. Divergência entre estoque e fiscal — coluna com underscore (B2_QATU)
SELECT B2.B2_COD, B2.B2_QATU,
  SUM(D3.D3_QUANT) AS QTD_MOV
FROM SB2010 B2
LEFT JOIN SD3010 D3
  ON D3.D3_FILIAL = B2.B2_FILIAL
  AND D3.D3_COD = B2.B2_COD
WHERE B2.B2_FILIAL = '01'
  AND B2.D_E_L_E_T_ = ' '
GROUP BY B2.B2_COD, B2.B2_QATU
HAVING B2.B2_QATU <> SUM(D3.D3_QUANT)

SQL puro × ADVPL: RetSqlName("SF3") é função ADVPL — ela não roda dentro do SSMS. Se o bloco é SQL puro, use a tabela física (SF3010); se é ADVPL, monte a string com RetSqlName(), passe por ChangeQuery() e execute via TCQUERY ... NEW ALIAS. E F3_EMISSAO LIKE '202608%' funciona, mas é não-SARGable: prefira BETWEEN.

Função de validação customizada

Exemplo de função ADVPL que roda as validações e retorna as divergências encontradas:

valida_sped.prw — Validação antes da transmissão
// valida_sped.prw — Valida o SPED antes da transmissão.
// Retorna array de divergências encontradas.
#Include "protheus.ch"
#Include "topconn.ch"

User Function VALIDASPED(cMes, cAno)
  Local aErros   := 
  Local cQuery   := ""
  Local cPeriodo := cAno + cMes

  // 1. Notas canceladas no período (ADVPL: RetSqlName + ChangeQuery + TCQUERY)
  cQuery := "SELECT F3_FILIAL, F3_NFISCAL, F3_SERIE, F3_CLIEFOR "
  cQuery += "FROM " + RetSqlName("SF3") + " SF3 "
  cQuery += "WHERE SF3.F3_FILIAL = '" + xFilial("SF3") + "' "
  cQuery += " AND SF3.F3_DTCANC <> '" + Space(8) + "' "
  cQuery += " AND SF3.F3_EMISSAO BETWEEN '" + cPeriodo + "01' AND '" + cPeriodo + "31' "
  cQuery += " AND SF3.D_E_L_E_T_ = ' ' "
  cQuery += " %nolock%"

  cQuery := ChangeQuery(cQuery)
  TCQUERY cQuery NEW ALIAS "QSFC"

  QSFC->(dbGoTop())
  While QSFC->(!Eof())
    aAdd(aErros, "Nota cancelada: " + ;
      QSFC->F3_NFISCAL + " / " + QSFC->F3_SERIE)
    QSFC->(dbSkip())
  End

  QSFC->(dbCloseArea())

  // 2. Período apurado: verifique nas tabelas de apuração/fechamento do seu
  // ambiente (MATA953 grava o resultado da apuração). Não existe um parâmetro
  // MV_ genérico "ICMS apurado" — controle é por tabela, não por GetMv.

  Return aErros

Controle de idempotência

Reprocessar o mesmo período não pode duplicar movimentos. A solução: tabela de controle com filial + período como chave, sem "hash mágico" — hash que depende da data de execução muda a cada rodada e não prova nada.

Controle de processamento SPED
// Tabela customizada ZZ_SPED
// Campos: ZZ_FILIAL, ZZ_PERIOD, ZZ_STATUS, ZZ_DTPROC

User Function SPEDOK(cPeriodo)
  Local lRet := .F.

  // Área + ordem corretas antes de buscar (índice: ZZ_FILIAL + ZZ_PERIOD)
  DbSelectArea("ZZ_SPED")
  ZZ_SPED->(DbSetOrder(1))

  // Verifica se já foi processado
  If ZZ_SPED->(MsSeek(xFilial("ZZ_SPED") + cPeriodo))
    If ZZ_SPED->ZZ_STATUS == "OK"
      // Já processado — log estruturado, sem MsgAlert (isso roda em job)
      FWLogMsg("WARN", "", "SPEDOK", "", "", "", ;
        "Periodo " + cPeriodo + " ja processado em " + DTOC(ZZ_SPED->ZZ_DTPROC))
      Return .F.
    EndIf
  EndIf

  // Grava registro de controle
  RecLock("ZZ_SPED", .T.)
  ZZ_SPED->ZZ_FILIAL := xFilial("ZZ_SPED")
  ZZ_SPED->ZZ_PERIOD := cPeriodo
  ZZ_SPED->ZZ_STATUS := "PROC"
  ZZ_SPED->ZZ_DTPROC := Date()
  MsUnLock()

  Return .T.

Padrões aplicados: campos em maiúsculas (ZZ_DTPROC, não ZZ_DT_proc), FWLogMsg em vez de interface (MsgAlert em rotina automática trava o job), e nomenclatura de campo validada no SX3 antes de gravar.

Checklist de validação antes da transmissão

A automação deve verificar tudo antes de transmitir. Cada item é uma query ou regra de negócio:

01

CFOP vs Operação

CFOP de saída (5xxx) em nota de entrada? CFOP de importação (3xxx) sem DI? A automação deve cruzar CFOP com tipo de operação.

02

CST/CSOSN consistente

CST do cadastro do produto (SB1) batendo com o CST da nota (SF3)? Divergência gera flag no EFD.

03

Valores batendo

Batimento SF2 × itens (SD2) com tolerância de centavos e regra por TES/CFGTRIB: frete, seguro, desconto e impostos mudam a composição. Fórmula simplificada demais gera falso positivo toda semana.

04

Base de cálculo ICMS

Base de cálculo do ICMS = valor da operação? Em operações com ST, a base é diferente. A query deve contemplar ambas.

05

IPI batendo

Valor do IPI calculado = valor do IPI informado na NF? Divergência pode indicar tabela de alíquotas desatualizada.

06

PIS/COFINS

Alíquota de PIS/COFINS do cadastro do produto batendo com o regime de apuração (cumulativo/não cumulativo)?

Cuidados de engenharia

  • Idempotência: reprocessar o mesmo período não pode duplicar movimentos. Use tabela de controle por filial + período.
  • Regressão fiscal: cada versão do Protheus pode mudar regras de apuração. Homologue sempre antes de produção.
  • Correção, não "rollback": apuração fiscal não tem "desfazer genérico" — a correção exige estorno/retificação conforme o período e a obrigação (EFD retificadora, guia complementar). Prometer rollback simples de apuração é perigoso.
  • Log completo: cada etapa deve gerar log com timestamp, registros processados e resultado. Auditoria exige isso.
  • EFD-Contribuições ≠ EFD ICMS/IPI: PIS/COFINS tem regras, leiaute e geração próprios — valide a rotina da sua release no menu Fiscal antes de automar; não misture os dois fluxos.

O objetivo não é "apertar um botão e esquecer". É transformar um processo crítico em algo rastreável, repetível e auditável — com o humano atuando onde importa: na decisão sobre divergência.

Automação fiscal que não falha na hora H?

Falar com a ELP