Função que retorna o xml de uma nota em AdvPL

Olá pessoal…

Hoje vou mostrar uma função que desenvolvi, que retorna o xml de uma nota transmitida ou cancelada via AdvPL.

AdvPL

AdvPL

A função utiliza recursos do TSS para carregar e criar o arquivo xml, é necessário apenas informar, o número da nota, a série, e o caminho para geração do arquivo xml.

Abaixo a função desenvolvida:

//Bibliotecas
#Include "Protheus.ch"
   
/*/{Protheus.doc} zSpedXML
Função que gera o arquivo xml da nota (normal ou cancelada) através do documento e da série disponibilizados
@author Atilio
@since 25/07/2017
@version 1.0
@param cDocumento, characters, Código do documento (F2_DOC)
@param cSerie, characters, Série do documento (F2_SERIE)
@param cArqXML, characters, Caminho do arquivo que será gerado (por exemplo, C:\TOTVS\arquivo.xml)
@param lMostra, logical, Se será mostrado mensagens com os dados (erros ou a mensagem com o xml na tela)
@type function
@example Segue exemplo abaixo
    u_zSpedXML("000000001", "1", "C:\TOTVS\arquivo1.xml", .F.) //Não mostra mensagem com o XML
       
    u_zSpedXML("000000001", "1", "C:\TOTVS\arquivo2.xml", .T.) //Mostra mensagem com o XML
/*/
   
User Function zSpedXML(cDocumento, cSerie, cArqXML, lMostra)
    Local aArea        := GetArea()
    Local cURLTss      := PadR(GetNewPar("MV_SPEDURL","http://"),250)  
    Local oWebServ
    Local cIdEnt       := RetIdEnti()
    Local cTextoXML    := ""
    Default cDocumento := ""
    Default cSerie     := ""
    Default cArqXML    := GetTempPath()+"arquivo_"+cSerie+cDocumento+".xml"
    Default lMostra    := .F.
       
    //Se tiver documento
    If !Empty(cDocumento)
        cDocumento := PadR(cDocumento, TamSX3('F2_DOC')[1])
        cSerie     := PadR(cSerie,     TamSX3('F2_SERIE')[1])
           
        //Instancia a conexão com o WebService do TSS    
        oWebServ:= WSNFeSBRA():New()
        oWebServ:cUSERTOKEN        := "TOTVS"
        oWebServ:cID_ENT           := cIdEnt
        oWebServ:oWSNFEID          := NFESBRA_NFES2():New()
        oWebServ:oWSNFEID:oWSNotas := NFESBRA_ARRAYOFNFESID2():New()
        aAdd(oWebServ:oWSNFEID:oWSNotas:oWSNFESID2,NFESBRA_NFESID2():New())
        aTail(oWebServ:oWSNFEID:oWSNotas:oWSNFESID2):cID := (cSerie+cDocumento)
        oWebServ:nDIASPARAEXCLUSAO := 0
        oWebServ:_URL              := AllTrim(cURLTss)+"/NFeSBRA.apw" 
           
        //Se tiver notas
        If oWebServ:RetornaNotas()
           
            //Se tiver dados
            If Len(oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3) > 0
               
                //Se tiver sido cancelada
                If oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3[1]:oWSNFECANCELADA != Nil
                    cTextoXML := oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3[1]:oWSNFECANCELADA:cXML
                       
                //Senão, pega o xml normal
                Else
                    cTextoXML := oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3[1]:oWSNFE:cXML
                EndIf
                   
                //Gera o arquivo
                MemoWrite(cArqXML, cTextoXML)
                   
                //Se for para mostrar, será mostrado um aviso com o conteúdo
                If lMostra
                    Aviso("zSpedXML", cTextoXML, {"Ok"}, 3)
                EndIf
                   
            //Caso não encontre as notas, mostra mensagem
            Else
                ConOut("zSpedXML > Verificar parâmetros, documento e série não encontrados ("+cDocumento+"/"+cSerie+")...")
                   
                If lMostra
                    Aviso("zSpedXML", "Verificar parâmetros, documento e série não encontrados ("+cDocumento+"/"+cSerie+")...", {"Ok"}, 3)
                EndIf
            EndIf
           
        //Senão, houve erros na classe
        Else
            ConOut("zSpedXML > "+IIf(Empty(GetWscError(3)), GetWscError(1), GetWscError(3))+"...")
               
            If lMostra
                Aviso("zSpedXML", IIf(Empty(GetWscError(3)), GetWscError(1), GetWscError(3)), {"Ok"}, 3)
            EndIf
        EndIf
    EndIf
    RestArea(aArea)
Return

Update Dezembro de 2021:

O grande Jorge Alberto ( LinkedIn ), fez uma contribuição para que seja exportado a tag NFeProc juntamente com a tag do XML principal, abaixo segue o exemplo (trecho alterado da linha 59 a 63):

//Bibliotecas
#Include "Protheus.ch"
   
/*/{Protheus.doc} zSpedXML
Função que gera o arquivo xml da nota (normal ou cancelada) através do documento e da série disponibilizados
@author Atilio
@since 25/07/2017
@version 1.0
@param cDocumento, characters, Código do documento (F2_DOC)
@param cSerie, characters, Série do documento (F2_SERIE)
@param cArqXML, characters, Caminho do arquivo que será gerado (por exemplo, C:\TOTVS\arquivo.xml)
@param lMostra, logical, Se será mostrado mensagens com os dados (erros ou a mensagem com o xml na tela)
@type function
@example Segue exemplo abaixo
    u_zSpedXML("000000001", "1", "C:\TOTVS\arquivo1.xml", .F.) //Não mostra mensagem com o XML
       
    u_zSpedXML("000000001", "1", "C:\TOTVS\arquivo2.xml", .T.) //Mostra mensagem com o XML
/*/
   
User Function zSpedXML(cDocumento, cSerie, cArqXML, lMostra)
    Local aArea        := GetArea()
    Local cURLTss      := PadR(GetNewPar("MV_SPEDURL","http://"),250)  
    Local oWebServ
    Local cIdEnt       := RetIdEnti()
    Local cTextoXML    := ""
    Default cDocumento := ""
    Default cSerie     := ""
    Default cArqXML    := GetTempPath()+"arquivo_"+cSerie+cDocumento+".xml"
    Default lMostra    := .F.
       
    //Se tiver documento
    If !Empty(cDocumento)
        cDocumento := PadR(cDocumento, TamSX3('F2_DOC')[1])
        cSerie     := PadR(cSerie,     TamSX3('F2_SERIE')[1])
           
        //Instancia a conexão com o WebService do TSS    
        oWebServ:= WSNFeSBRA():New()
        oWebServ:cUSERTOKEN        := "TOTVS"
        oWebServ:cID_ENT           := cIdEnt
        oWebServ:oWSNFEID          := NFESBRA_NFES2():New()
        oWebServ:oWSNFEID:oWSNotas := NFESBRA_ARRAYOFNFESID2():New()
        aAdd(oWebServ:oWSNFEID:oWSNotas:oWSNFESID2,NFESBRA_NFESID2():New())
        aTail(oWebServ:oWSNFEID:oWSNotas:oWSNFESID2):cID := (cSerie+cDocumento)
        oWebServ:nDIASPARAEXCLUSAO := 0
        oWebServ:_URL              := AllTrim(cURLTss)+"/NFeSBRA.apw" 
           
        //Se tiver notas
        If oWebServ:RetornaNotas()
           
            //Se tiver dados
            If Len(oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3) > 0
               
                //Se tiver sido cancelada
                If oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3[1]:oWSNFECANCELADA != Nil
                    cTextoXML := oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3[1]:oWSNFECANCELADA:cXML
                       
                //Senão, pega o xml normal (foi alterado abaixo conforme dica do Jorge Alberto)
                Else
                    cTextoXML := '<?xml version="1.0" encoding="UTF-8"?>'
                    cTextoXML += '<nfeProc xmlns="http://www.portalfiscal.inf.br/nfe" versao="4.00">'
                    cTextoXML += oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3[1]:oWSNFE:cXML
                    cTextoXML += oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3[1]:oWSNFE:cXMLPROT
                    cTextoXML += '</nfeProc>'
                EndIf
                   
                //Gera o arquivo
                MemoWrite(cArqXML, cTextoXML)
                   
                //Se for para mostrar, será mostrado um aviso com o conteúdo
                If lMostra
                    Aviso("zSpedXML", cTextoXML, {"Ok"}, 3)
                EndIf
                   
            //Caso não encontre as notas, mostra mensagem
            Else
                ConOut("zSpedXML > Verificar parâmetros, documento e série não encontrados ("+cDocumento+"/"+cSerie+")...")
                   
                If lMostra
                    Aviso("zSpedXML", "Verificar parâmetros, documento e série não encontrados ("+cDocumento+"/"+cSerie+")...", {"Ok"}, 3)
                EndIf
            EndIf
           
        //Senão, houve erros na classe
        Else
            ConOut("zSpedXML > "+IIf(Empty(GetWscError(3)), GetWscError(1), GetWscError(3))+"...")
               
            If lMostra
                Aviso("zSpedXML", IIf(Empty(GetWscError(3)), GetWscError(1), GetWscError(3)), {"Ok"}, 3)
            EndIf
        EndIf
    EndIf
    RestArea(aArea)
Return

Update Fevereiro de 2022:

O grande Josuel Silva ( LinkedIn ), fez uma adaptação no fonte para usar a classe FWFileWriter ao invés de MemoWrite, assim podendo usar a codificação UTF8. Segue abaixo:

//Bibliotecas
#Include "Protheus.ch"
   
/*/{Protheus.doc} zSpedXML
Função que gera o arquivo xml da nota (normal ou cancelada) através do documento e da série disponibilizados
@author Atilio
@since 25/07/2017
@version 1.0
@param cDocumento, characters, Código do documento (F2_DOC)
@param cSerie, characters, Série do documento (F2_SERIE)
@param cArqXML, characters, Caminho do arquivo que será gerado (por exemplo, C:\TOTVS\arquivo.xml)
@param lMostra, logical, Se será mostrado mensagens com os dados (erros ou a mensagem com o xml na tela)
@type function
@example Segue exemplo abaixo
    u_zSpedXML("000000001", "1", "C:\TOTVS\arquivo1.xml", .F.) //Não mostra mensagem com o XML
       
    u_zSpedXML("000000001", "1", "C:\TOTVS\arquivo2.xml", .T.) //Mostra mensagem com o XML
/*/
   
User Function zSpedXML(cDocumento, cSerie, cArqXML, lMostra)
    Local aArea        := GetArea()
    Local cURLTss      := PadR(GetNewPar("MV_SPEDURL","http://"),250)  
    Local oWebServ
    Local cIdEnt       := RetIdEnti()
    Local cTextoXML    := ""
	Local oFileXML
    Default cDocumento := ""
    Default cSerie     := ""
    Default cArqXML    := GetTempPath()+"arquivo_"+cSerie+cDocumento+".xml"
    Default lMostra    := .F.
       
    //Se tiver documento
    If !Empty(cDocumento)
        cDocumento := PadR(cDocumento, TamSX3('F2_DOC')[1])
        cSerie     := PadR(cSerie,     TamSX3('F2_SERIE')[1])
           
        //Instancia a conexão com o WebService do TSS    
        oWebServ:= WSNFeSBRA():New()
        oWebServ:cUSERTOKEN        := "TOTVS"
        oWebServ:cID_ENT           := cIdEnt
        oWebServ:oWSNFEID          := NFESBRA_NFES2():New()
        oWebServ:oWSNFEID:oWSNotas := NFESBRA_ARRAYOFNFESID2():New()
        aAdd(oWebServ:oWSNFEID:oWSNotas:oWSNFESID2,NFESBRA_NFESID2():New())
        aTail(oWebServ:oWSNFEID:oWSNotas:oWSNFESID2):cID := (cSerie+cDocumento)
        oWebServ:nDIASPARAEXCLUSAO := 0
        oWebServ:_URL              := AllTrim(cURLTss)+"/NFeSBRA.apw" 
           
        //Se tiver notas
        If oWebServ:RetornaNotas()
           
            //Se tiver dados
            If Len(oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3) > 0
               
                //Se tiver sido cancelada
                If oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3[1]:oWSNFECANCELADA != Nil
                    cTextoXML := oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3[1]:oWSNFECANCELADA:cXML
                       
                //Senão, pega o xml normal (foi alterado abaixo conforme dica do Jorge Alberto)
                Else
                    cTextoXML := '<?xml version="1.0" encoding="UTF-8"?>'
                    cTextoXML += '<nfeProc xmlns="http://www.portalfiscal.inf.br/nfe" versao="4.00">'
                    cTextoXML += oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3[1]:oWSNFE:cXML
                    cTextoXML += oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3[1]:oWSNFE:cXMLPROT
                    cTextoXML += '</nfeProc>'
                EndIf
                   
                //Gera o arquivo
				oFileXML := FWFileWriter():New(cArqXML, .T.)
				oFileXML:SetEncodeUTF8(.T.)
				oFileXML:Create()
				oFileXML:Write(cTextoXML)
				oFileXML:Close()
                   
                //Se for para mostrar, será mostrado um aviso com o conteúdo
                If lMostra
                    Aviso("zSpedXML", cTextoXML, {"Ok"}, 3)
                EndIf
                   
            //Caso não encontre as notas, mostra mensagem
            Else
                ConOut("zSpedXML > Verificar parâmetros, documento e série não encontrados ("+cDocumento+"/"+cSerie+")...")
                   
                If lMostra
                    Aviso("zSpedXML", "Verificar parâmetros, documento e série não encontrados ("+cDocumento+"/"+cSerie+")...", {"Ok"}, 3)
                EndIf
            EndIf
           
        //Senão, houve erros na classe
        Else
            ConOut("zSpedXML > "+IIf(Empty(GetWscError(3)), GetWscError(1), GetWscError(3))+"...")
               
            If lMostra
                Aviso("zSpedXML", IIf(Empty(GetWscError(3)), GetWscError(1), GetWscError(3)), {"Ok"}, 3)
            EndIf
        EndIf
    EndIf
    RestArea(aArea)
Return

Bom pessoal, por hoje é só.
Abraços e até a próxima.

Dan (Daniel Atilio)
Especialista em Engenharia de Software pela FIB. Entusiasta de soluções Open Source. E blogueiro nas horas vagas.

28 Responses

  1. William Correia disse:

    Muito bom, obrigado por compartilhar seu conhecimento, estou aprendendo muito aqui no seu site.
    Tem uma documentação completa sobre a WSNFeSBRA? ou como faço pra ver todos os Metodos dela?

    • Dan_Atilio disse:

      Bom dia William, tudo bem?
      Por ser uma classe padrão, apenas a TOTVS possui a documentação dela, se você for um cliente, você pode solicitar por chamado. Mas não sei se passariam isso.
      Agora, se você for um analista da TOTVS, se tiver a chave de ouro, procure pelo .prx e no fonte padrão estará os métodos dela.
      Um grande abraço.

  2. Emerson Nascimento disse:

    Não testei, mas achei sensacional pela simplicidade do código, pelo uso dos recursos da linguagem, sem reinvenção da “roda”. Obrigado por compartilhar.

  3. André Felipe Loos disse:

    Boa tarde!
    Estou precisando de uma função parecida, mas já tenho o XML e preciso gerar o PDF. Teria alguma opinião para o que pode ser feito?

    • Dan_Atilio disse:

      Bom dia André, tudo bem?
      Claro, o que você pode fazer, seria +- assim:
      1 – Criar uma tela de parâmetros com o caminho do xml de origem
      2 – No seu fonte, usar XMLParser / XMLParserFile para converter o texto em um objeto
      3 – Instanciar um FWMSPrinter em um objeto
      4 – Percorrer os nós do XML no objeto e imprimir no objeto de impressão

  4. GERALDO disse:

    Consigo gerar o XML mas vem sem PROTOCOLO DE AUTORIZAÇÃO DE USO ? Alguem com este problema nao consigo acertar o codigo…

  5. GERALDO disse:

    SIGAMDI , VOU TESTAR PELO SIGAADV e sim ja trasmitida e autorizada quando uso o exportar padrao do TSS ele exporta correto … estava subindo novo release mas vou retomar este assunte mas por enquanto muito obrigado todos os posts aqui so Fera … Vlw

  6. Vitor Saito disse:

    Bom dia Dan_Atilio! Utilizei seu fonte para efetuar o download do XML, porém, alguns XML’s não vem no padrão correto. Alguns ele baixa normalmente, mas outros vem com informações ‘cortadas’ e vem tudo em uma mesma linha. Quando abro em .TXT ele traz normalmente as informações. Pode me dizer o porquê? Obrigado.

    • Dan_Atilio disse:

      Bom dia Vitor.
      O de trazer na mesma linha, é similar a você ir no Protheus, na tela de monitorar, pegar o schema e colar.
      Ai para quebrar as linhas, você pode usar um plugin no Notepad++, o XML Tools (opção Pretty Print XML With Line Breaks).
      Quanto ao conteúdo vir cortado, não sei te dizer, não peguei casos assim. Sempre corta com um número exato de caracteres?

  7. Thiago disse:

    Olá Danilo.

    Teria alguma função para emitir NFs de entrada?
    Essa só gera de NFs de saída.

  8. WILKER MARCAL FARIA COSTA disse:

    Bom dia Dan_Atilio
    Estou usando em um cliente a funcao que retorna o xml
    e no meu caso esta gerando sem o protoloco de autorizacao.
    debugando o fonte na classe que carrega o gera o xml, nao esta gerando com o protocolo mesmo
    Tem ideia do que pode ser ?

    • Dan_Atilio disse:

      Bom dia Wilker.
      Se você fizer o processo manualmente funciona?
      Por exemplo, abrir o SIGAMDI, ir em Faturamento, Nfe Sefaz, Outras Ações, Monitorar, Inserir o número da Nota, clicar em outras ações, e depois clicar em Schema e salvar o XML. Dessa forma traz o protocolo?
      Abraços.

  9. Miziara disse:

    Aos colegas que estão com o problema do xml ser gerado sem o protocolo de uso, substitua a linha 59 pelo trecho abaixo:

    //Senão, pega o xml normal
                    Else
                        cTextoXML := '<?xml version="1.0" encoding="UTF-8"?> <nfeProc xmlns="http://www.portalfiscal.inf.br/nfe" versao="4.00">'
                        cTextoXML += oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3[1]:oWSNFE:cXML
                        cTextoXML += oWebServ:oWsRetornaNotasResult:OWSNOTAS:oWSNFES3[1]:oWSNFE:cXMLPROT
                        cTextoXML += '</nfeProc>'
                    EndIf
    
  10. Guilherme Lopes disse:

    Queria saber se tem algum parâmetro que pode ser informado para pesquisar no ambiente de de produção ou homologação

  11. Josuel Silva disse:

    Fiz uma pequena alteração no fonte no momento da gravação substituído a função MemoWrite pela Classe FWFileWriter.
    Desta forma, caso o XML contenha algum carácter UTF8, é possível setar a classe para gravar nesse formato e não deixar o XML inválido.

    Alterado de :
    MemoWrite(cArqXML, cTextoXML)

    Para:

    oFileXML := FWFileWriter():New(cArqXML, .T.)
    oFileXML:SetEncodeUTF8(.T.)
    oFileXML:Create()
    oFileXML:Write(cTextoXML)
    oFileXML:Close()

    Isso me ajudou a corrigir um problema na geração do XML devido descrição de condição de pagamento:

    Antes:

    Negocia��o Futura

    Depois:

    Negociação Futura

    Grato

  12. Thiago disse:

    Essa função RetIdEnti() ela é nativa ou você implementou ela Daniel?

  13. ThiagoM disse:

    Olá, Daniel.
    Tenho uma dúvida, essa função para obter o IdEnt é nativa ou você desenvolveu ela? RetIdEnti()

  14. Josuel Silva disse:

    Olá @ThiagoM,
    esta função é nativa e retorna o codigo da entidade que será enviado para o TSS.
    Basicamente para ela relaciona o codigo que está relacionado a Filial logada.

    Grato.

  15. Rodolfo Novaes de Sousa disse:

    boa noite! muito boa a função!
    estou com um problema, utilizando ela via menu no sigamdi funcionou perfeitamente. Porem ao fazer a chamada a partir de uma API Rest , retorna que não achou a NF “zSpedXML > Verificar parâmetros, documento e série não encontrados” .

  16. esse webservice baixa notas que nao esteja no sped050?

Deixe uma resposta