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.