Transmitir e monitorar NFe via código fonte

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:

  1. É recebido por parâmetro a nota e série da SF2
  2. É feito a busca do ident, ambiente e modalidade
  3. Se conseguiu buscar as informações aciona a SpedNfeTrf para transmitir
  4. Em seguida é acionado a ProcMonitorDoc
  5. 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.

Dan (Daniel Atilio)
Cristão de ramificação protestante. Especialista em Engenharia de Software pela FIB, graduado em Banco de Dados pela FATEC Bauru e técnico em informática pelo CTI da Unesp. Entusiasta de soluções Open Source e blogueiro nas horas vagas. Autor e mantenedor do portal Terminal de Informação.

8 Responses

  1. Luan Menezes disse:

    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.

      • Caio Lesnock disse:

        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.

  2. Daniel disse:

    Boa tarde, Daniel!

    Uma duvida: esse exemplo serviria também para transmissão de NFSe(serviço)?

  3. Pablo dos Santos Neves disse:

    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

Deixe uma resposta

Terminal de Informação