No artigo de hoje vamos demonstrar em como transmitir e monitorar uma NFe de saída via comandos em AdvPL ou TLPP.
Recentemente nos perguntaram se seria possível numa customização, acionar a transmissão do documento e monitorá-lo, sem exibir telas aos usuários.
Pensando nisso, montamos esse exemplo onde:
- É recebido por parâmetro a nota e série da SF2
- É feito a busca do ident, ambiente e modalidade
- Se conseguiu buscar as informações aciona a SpedNfeTrf para transmitir
- Em seguida é acionado a ProcMonitorDoc
- Se deu tudo certo, ou algum erro, é definido num array que será retornado
Supondo então, que você tenha a nota 000123789 com a série 1, pra acionar a customização iria ficar:
aInfo := u_zTransNfe("000123789", "1")
E abaixo o código completo da zTransNfe:
//Bibliotecas
#Include "TOTVS.ch"
/*/{Protheus.doc} zTransNfe
Função para transmitir a SF2 para a SEFAZ e Monitorar o resultado
@type user function
@author Atilio
@since 13/06/2025
@version version
@param cDocumento, Caractere, Número do Documento que vai ser transmitido
@param cSerie, Caractere, Série do Documento
@return aRetorno, Array, Array com duas posição, primeira se deu certo (.T. ou .F.) e segunda a mensagem (com observações)
@example
aInfo := u_zTransNfe("000123456", "1")
/*/
User Function zTransNfe(cDocumento, cSerie)
Local aArea := FWGetArea()
//Transmissão e Monitoramento
Local cModelo := "55"
Local cIdEnt := ""
Local cError := ""
//Transmissão
Local cAmbiente := ""
Local cModalidade := ""
Local cVersao := ""
Local lCte := .F. //( cModelo == "57" )
Local lEnd := .F.
//Monitoramento
Local nVezes := 0
Local cURL := PadR(GetNewPar("MV_SPEDURL", "http://"), 250)
Local aParam := {}
Local nTpMonitor := 1
Local aRetMonit := {}
//Retorno da Função
Local lDeuCerto := .F.
Local cMensagem := ""
Local aRetorno := {.F., ""}
//Posiciona na Nota (que não tenha chave de acesso ainda)
Dbselectarea('SF2')
SF2->(DbSetOrder(1)) // F2_FILIAL + F2_DOC + F2_SERIE + F2_CLIENTE + F2_LOJA + F2_FORMUL + F2_TIPO
If SF2->(MsSeek(FWxFilial('SF2') + cDocumento + cSerie)) .And. Empty(SF2->F2_CHVNFE)
cIdEnt := getCfgEntidade(@cError)
//Se encontrou a entidade
If ! Empty( cIdEnt )
//Busca o ambiente, modalidade e versão
cAmbiente := getCfgAmbiente(@cError, cIdEnt, cModelo)
If ! Empty(cAmbiente)
cModalidade := getCfgModalidade(@cError, cIdEnt, cModelo)
If ! Empty(cModalidade)
cVersao := getCfgVersao(@cError, cIdEnt, cModelo)
EndIf
EndIf
//Se conseguiu buscar as informações
If ! Empty(cVersao)
//Aciona a transmissão da Nota para o Sefaz
cRetorno := SpedNFeTrf(;
"SF2",; //cAlias
SF2->F2_SERIE,; //cSerie
SF2->F2_DOC,; //cNotaIni
SF2->F2_DOC,; //cNotaFim
cIdEnt,; //cIDEnt
cAmbiente,; //cAmbiente
cModalidade,; //cModalidade
cVersao,; //cVersao
@lEnd,; //lEnd
lCte,; //lCte
.T.,; //lAuto
Nil,; //dDataDe
Nil; //dDataAte
)
//Se tiver a mensagem de sucesso, tenta acionar o monitoramento
If "SUCESSO" $ Upper(cRetorno)
//Monta o array de parâmetros para monitorar
aParam := Array(5)
aParam[1] := SF2->F2_SERIE
aParam[2] := SF2->F2_DOC
aParam[3] := SF2->F2_DOC
aParam[4] := MonthSub(SF2->F2_EMISSAO, 1)
aParam[5] := MonthSum(SF2->F2_EMISSAO, 1)
//Tenta monitorar 3 vezes
While nVezes < 3
aRetMonit := procMonitorDoc(;
cIdent,; //cIdEnt
cUrl,; //cUrl
aParam,; //aParam
nTpMonitor,; //nTpMonitor (1=Range de Notas; 2=Lote de Id;3=Tempo)
"0" + cModelo,; //cModelo
lCte,; //lCte
@cError; //cAviso
)
//Se teve retorno o monitoramento
If Len(aRetMonit) > 0
//Se começar com 001, deu certo o monitoramento
If Left(Alltrim(aRetMonit[1][9]), 3) == '001'
lDeuCerto := .T.
cMensagem := "Sucesso na transmissão e monitoramento da Nota!"
Exit
EndIf
EndIf
//Aguarda 3 segundos antes da próxima tentativa
Sleep(3000)
nVezes++
EndDo
//Se mesmo ao acionar o monitoramento, não der certo, atualiza a variável de retorno
If ! lDeuCerto
cMensagem := "Falha ao tentar acionar o monitoramento! " + cError
EndIf
Else
cMensagem := "Falha na transmissão da Nota! " + cRetorno
EndIf
Else
cMensagem := "Falha ao buscar informações (Ambiente, Modalidade e Versão)! " + cError
EndIf
Else
cMensagem := "Falha ao buscar o IdEnt da Empresa! " + cError
EndIf
Else
cMensagem := "Nota e Série não encontradas na SF2!"
EndIf
//Define o retorno
aRetorno[1] := lDeuCerto
aRetorno[2] := cMensagem
FWRestArea(aArea)
Return aRetorno
Bom pessoal, por hoje é só.
Abraços e até a próxima.
Parabéns pelo post, mais uma ótima ideia. É possível fazer esse processo para as notas que foram canceladas? Sempre acontece do usuário cancelar a nota e não ir no monitor.
Bom dia Luan, tudo joia?
Já tem uma tratativa no padrão do sistema para isso.
No parâmetro MV_CANCNFE, você altera ele para .T.
Ai ao tentar cancelar uma nota, ele vai tentar fazer a transmissão do cancelamento e monitoramento.
Depois nos dê o feedback se deu certo ai.
Tenha uma ótima e abençoada sexta feira.
Um forte abraço.
Estou com esse mesmo problema.
Pelo smartclient ele transmite a nota cancelada normalmente, mas com essa função, apesar de ele mostrar “sucesso”, quando monitoro ainda não cancelou.
No console.log eu vi que ele está fazendo essa query:
SELECT F3_FILIAL, F3_ENTRADA, F3_NFELETR, F3_CFO, F3_FORMUL, F3_NFISCAL, F3_SERIE, F3_CLIEFOR, F3_LOJA, F3_ESPECIE, F3_DTCANC, F3_OBSERV, F3_CHVNFE, F3_CODNFE, F3_CODRSEF
FROM SF3010 WHERE F3_FILIAL = ’01’ AND ((SUBSTRING(F3_CFO,1,1) = ‘5’))
AND F3_SERIE = ‘1 ‘ AND F3_ESPECIE = ‘SPED’ AND F3_NFISCAL >= ‘000062672’ AND F3_NFISCAL <= '000062672' AND F3_DTCANC = ' ' AND D_E_L_E_T_ = ' ' ORDER BY F3_NFISCAL
Acredito que acondição "F3_DTCANC = ' ' " deve estar causando esse problema.
Bom dia Caio, tudo joia?
Essa função foi criada para transmissão de NFs normais e monitoramento, não foi criada para o processo de cancelamento de NFs.
Tem um IF na linha 43, que somente vai permitir prosseguir, se encontrar o registro na SF2 e se ele não tiver chave ainda.
Dito isso, pode ser que não seja então a SpedNFeTrf, pode ser que para transmissão de cancelamento seja outra função. Ou pode ser que seja outras parametrizações nela.
Mas se surgir a oportunidade em algum cliente, ai tentaremos desenvolver para essa parte de transmissão de cancelamentos e atualizamos o exemplo.
Até lá, poderia tentar usar o padrão, que é com o MV_CANCNFE.
Tenha um ótimo e abençoado fim de semana.
Um forte abraço.
Boa tarde, Daniel!
Uma duvida: esse exemplo serviria também para transmissão de NFSe(serviço)?
Bom dia xará, tudo joia?
Não cheguei a testar com NFSe. Mas acredito que são recursos e funcionalidades diferentes.
Tenha um ótimo e abençoado fim de semana.
Um forte abraço.
Boa tarde, tudo bem?
Eu tentei usar mas quando executo via schedule da esse erro. Já viu algo sobre?
THREAD ERROR ([20620], SCH_CONTROLLER|10.27.11.55|1310_, THIS) 13/02/2026 09:09:46
argument #0 error, expected C->L, function substr in file E:\build-dir\TP11-OF2431X-W6\advpl\parmiv.hpp at line 106
on SPEDNFETRF(SPEDNFE.PRX) 31/07/2025 11:35:19 line : 1772
Bom dia Pablo, tudo joia graças a Deus e você?
No caso você criou uma User Function, dentro dela usou o RPCSetEnv, buscou as notas na SF2 que faltavam ser transmitidas e aí acionou a customização zTransNfe?
Se algum dos passos acima não foi feito, ai pode ser essa a causa da mensagem.
Lembrando que, tem o recurso padrão para transmissão de documentos via Schedule: https://centraldeatendimento.totvs.com/hc/pt-br/articles/360029358971-Cross-Segmentos-Backoffice-Protheus-Doc-Eletr%C3%B4nicos-NF-e-AUTONFE-Transmiss%C3%A3o-Monitoramento-e-Cancelamento-Autom%C3%A1ticos
Tenha um ótimo e abençoado fim de semana.
Um forte abraço.