Função para gerar DANFE e XML de uma nota em uma pasta via AdvPL

Você já precisou gerar o PDF e o XML de uma nota em uma pasta qualquer?

Basicamente, nessa função desenvolvida, você passa por parâmetro o número da nota, a série e a pasta em que quer os arquivos gerados. A partir disso, é chamado a função zSpedXML (Função que retorna o xml de uma nota em AdvPL). Depois disso, é acessado o RDMake da DANFEII e gerado o pdf da danfe.

Abaixo o código fonte desenvolvido:

//Bibliotecas
#Include "Protheus.ch"
#Include "TBIConn.ch" 
#Include "Colors.ch"
#Include "RPTDef.ch"
#Include "FWPrintSetup.ch"
  
/*/{Protheus.doc} zGerDanfe
Função que gera a danfe e o xml de uma nota em uma pasta passada por parâmetro
@author Atilio
@since 10/02/2019
@version 1.0
@param cNota, characters, Nota que será buscada
@param cSerie, characters, Série da Nota
@param cPasta, characters, Pasta que terá o XML e o PDF salvos
@type function
@example u_zGerDanfe("000123ABC", "1", "C:\TOTVS\NF")
@obs Para o correto funcionamento dessa rotina, é necessário:
    1. Ter baixado e compilado o rdmake danfeii.prw
    2. Ter baixado e compilado o zSpedXML.prw - https://terminaldeinformacao.com/2017/12/05/funcao-retorna-xml-de-uma-nota-em-advpl/
/*/
User Function zGerDanfe(cNota, cSerie, cPasta)
    Local aArea     := GetArea()
    Local cIdent    := ""
    Local cArquivo  := ""
    Local oDanfe    := Nil
    Local lEnd      := .F.
    Local nTamNota  := TamSX3('F2_DOC')[1]
    Local nTamSerie := TamSX3('F2_SERIE')[1]
    Local dDataDe   := sToD("20190101")
    Local dDataAt   := Date()
    Private PixelX
    Private PixelY
    Private nConsNeg
    Private nConsTex
    Private oRetNF
    Private nColAux
    Default cNota   := ""
    Default cSerie  := ""
    Default cPasta  := GetTempPath()
      
    //Se existir nota
    If ! Empty(cNota)
        //Pega o IDENT da empresa
        cIdent := RetIdEnti()
          
        //Se o último caracter da pasta não for barra, será barra para integridade
        If SubStr(cPasta, Len(cPasta), 1) != "\"
            cPasta += "\"
        EndIf
          
        //Gera o XML da Nota
        cArquivo := cNota + "_" + dToS(Date()) + "_" + StrTran(Time(), ":", "-")
        u_zSpedXML(cNota, cSerie, cPasta + cArquivo  + ".xml", .F.)
          
        //Define as perguntas da DANFE
        Pergunte("NFSIGW",.F.)
        MV_PAR01 := PadR(cNota,  nTamNota)     //Nota Inicial
        MV_PAR02 := PadR(cNota,  nTamNota)     //Nota Final
        MV_PAR03 := PadR(cSerie, nTamSerie)    //Série da Nota
        MV_PAR04 := 2                          //NF de Saida
        MV_PAR05 := 1                          //Frente e Verso = Sim
        MV_PAR06 := 2                          //DANFE simplificado = Nao
        MV_PAR07 := dDataDe                    //Data De
        MV_PAR08 := dDataAt                    //Data Até
          
        //Cria a Danfe
        oDanfe := FWMSPrinter():New(cArquivo, IMP_PDF, .F., , .T.)
          
        //Propriedades da DANFE
        oDanfe:SetResolution(78)
        oDanfe:SetPortrait()
        oDanfe:SetPaperSize(DMPAPER_A4)
        oDanfe:SetMargin(60, 60, 60, 60)
          
        //Força a impressão em PDF
        oDanfe:nDevice  := 6
        oDanfe:cPathPDF := cPasta                
        oDanfe:lServer  := .F.
        oDanfe:lViewPDF := .F.
          
        //Variáveis obrigatórias da DANFE (pode colocar outras abaixo)
        PixelX    := oDanfe:nLogPixelX()
        PixelY    := oDanfe:nLogPixelY()
        nConsNeg  := 0.4
        nConsTex  := 0.5
        oRetNF    := Nil
        nColAux   := 0
          
        //Chamando a impressão da danfe no RDMAKE
        RptStatus({|lEnd| u_DanfeProc(@oDanfe, @lEnd, cIdent, , , .F.)}, "Imprimindo Danfe...")
        oDanfe:Print()
    EndIf
      
    RestArea(aArea)
Return

Caso precise gerar várias notas, basta fazer um while ou for com os dados e chamar a função zGerDanfe, abaixo um exemplo com for:

Local nAtual := 0
Local aNotas := {}
  
//            Nota        Série Pasta
aAdd(aNotas, {"000000001", "1", "C:\TOTVS\NF"})
aAdd(aNotas, {"000000002", "1", "C:\TOTVS\NF"})
aAdd(aNotas, {"000000003", "1", "C:\TOTVS\NF"})
aAdd(aNotas, {"000000004", "1", "C:\TOTVS\NF"})
aAdd(aNotas, {"000000005", "1", "C:\TOTVS\NF"})
aAdd(aNotas, {"000000006", "1", "C:\TOTVS\NF"})
aAdd(aNotas, {"000000007", "1", "C:\TOTVS\NF"})
aAdd(aNotas, {"000000008", "1", "C:\TOTVS\NF"})
  
For nAtual := 1 To Len(aNotas)
    u_zGerDanfe(aNotas[nAtual][01], aNotas[nAtual][02], aNotas[nAtual][03])
Next

Abaixo um Exemplo com While:

//Monta a consulta e executa
cQuery := " SELECT F2_DOC, F2_SERIE ...."
TCQuery cQuery New Alias "QRY"
  
//enquanto houver dados
While ! QRY->(EoF())
  
    //chama a rotina
    u_zGerDanfe(QRY->F2_DOC, QRY->F2_SERIE, "C:\TOTVS\NF")
  
    //pula registro
    QRY->(DbSkip())
EndDo

Atenção – Update 2020

Se o seu arquivo estiver sendo gerado com 1 kb, no pacote RDMAKE novo disponibilizado pela TOTVS, verifique a função DanfeProc. Nas versões antigas, ela era uma Static Function (por isso usamos o StaticCall), como ela é uma User Function agora, pode-se utilizar diretamente u_DanfeProc.

Atenção – Update 2022

Se o seu arquivo estiver sendo gerado com conteúdo vazio, a TOTVS criou dois parâmetros novos no NFSIGW de Data De e Data Até, os MV_PAR07 e MV_PAR08, eu atualizei o fonte acima informando esses dois, revise o seu fonte caso esteja com esse mesmo problema.

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.

58 Responses

  1. Fabrício Antunes disse:

    Boa tarde teria como ao invés de passar o número da chave fazer uma pesquisa por data de emissão e baixar várias notas de uma vez?

  2. Saimon disse:

    Boa Noite Dan

    Obrigado pelas dicas e ajudas que proporciona para todos!.
    Mas estou com um problema, neste fonte da geração da danfe, no momento que executa o trecho do código “oDanfe:Print()”, abre a tela das perguntas da rotina, sendo que dentro tem “Pergunte(“NFSIGW”,.F.)”, sabe o que pode ser ?

    Valeuu

    • Dan_Atilio disse:

      Boa noite Saimon.
      Nos ambientes que testei, funcionou, pode ser alguma coisa especifica do seu danfeii.prw.
      Dentro do fonte, veja onde tem Pergunte(“…”, .T.) para fazer testes.
      Qualquer coisa, entre no nosso fórum do Discord, para podermos te ajudar.
      Abraços.

  3. Fabio F disse:

    show!! código perfeito e ainda usa o DANFEII padrão. valeu!

  4. RODRIGO TEIXEIRA DE SOUZA disse:

    Olá Dan,

    Obrigado por compartilhar conhecimento e dispor tempo em produzir o material.

    Estou à procura de alguma rotina padrão ou customizada que gere o DANFE a partir de um XML que recebi de um fornecedor, você já desenvolveu algo assim ou sabe se no Protheus tenha no Padrão ?

    Grande Abraço
    Rodrigo Teixeira

  5. Fco.C.Dantas disse:

    Eu apliquei os fontes, mas está gerando o PDF com apenas 1k e vazio.

    Ja teve essa situação ?

  6. Fco.C.Dantas disse:

    Aha! Achei o problema. No Fonte tá chamando a DANFEPROC como Static e ela é USER.
    Obrigado.

  7. Francisco disse:

    Porque todas danfe que eu tento gera apenas 1kb

  8. Tiago Badoco disse:

    Bom dia,

    o RDMAKE que estou utilizando a DanfeProc ainda é uma Static Function porém só gera arquivos com 1kb. Tem alguma outra alternativa?

    • Dan_Atilio disse:

      Boa tarde Tiago.
      Nesse caso, acho que só debugando o código mesmo.
      Coloca um Breakpoint logo no começo do DanfeProc e um logo antes do Return, e veja se o objeto está sendo manipulado.
      Grande abraço.

  9. Wagner Neves disse:

    Dan

    Excelente material. Teria uma função que leia um arquivo XML (XML DO FORNECEDOR) e imprima uma DANFE em PDF ?

    Obrigado

    Abraço
    Wagner

  10. Wagner Neves disse:

    Dan, excelente material.

    Por um acaso você teria uma rotina que lê o arquivo XML (FORNECEDOR) em uma determinada pasta e gere uma DANFE em PDF ?

    Obrigado

    Wagner

  11. Thiago disse:

    Danilo para corrigir o problema de 1k teria que chamar direto ficando assim?
    RPTStatus({|lEnd| U_DANFEProc(@oDanfe, @lEnd, cIDEnt, , , .F.,,,)}, “Imprimindo DANFE…” )

    Mas mesmo assim não deu certo, poderia me auxiliar?

    • Dan_Atilio disse:

      Então Thiago, acho que não precisa de RPTStatus, pode chamar direto, por exemplo:
      U_DANFEProc(@oDanfe, , cIDEnt, Nil, Nil, @lExistNFe, lIsLoja,,lAutomato )

      Essa linha acima peguei direto do fonte DANFEII do RDMAKE.

  12. Thiago disse:

    Dantas poderia compartilhar sua modificações nas linhas:
    //Chamando a impressão da danfe no RDMAKE
    RptStatus({|lEnd| StaticCall(DANFEII, DanfeProc, @oDanfe, @lEnd, cIdent, , , .F.)}, “Imprimindo Danfe…”)
    oDanfe:Print()

    Pois a que fiz não está dando certo

  13. GERALDO disse:

    Aguem ai esta com erro de gerar somente XML sem autorizacao ? pela funcao exportar do TSS ele traz completo

  14. Roberto Mendes disse:

    Bom dia Dan estou com o mesmo problema do Geraldo, estou executando chamando pelo SIGAMDI e gera o XML sem a autorização.

  15. Gustavo Manzali disse:

    Boa tarde Atilio, beleza?
    Queria agradecer por compartilhar seu conhecimento com a gente de forma clara é simples, isso ajuda muito, utilizo e recomendo seu site para muitos de meus amigos que compartilham do mesmo problema.

    Para quem teve problemas com a nova chamada gerando o danfe com arquivo vazio a chamada que funcionou foi essa.

    RPTStatus( {|lEnd| U_DANFEProc(@oDanfe, @lEnd, cIDEnt, Nil, Nil, .F., .F.,,.F. )}, “Imprimindo DANFE…” )

    Vlw Flw

  16. GILDESIO CAMPOS disse:

    Boa Tarde Atilio, utilizar sua rotinas para gerar XML e DANFE, mas não estou conseguindo imprimir o DANFE quando a rotina é chamada via agendamento/schedule. Voce, com sua experiencia, já fez a impressão do danfe via schedule ?
    Grande abraço

  17. Elson disse:

    Parabéns pela ferramenta, estou usando, fiz aquela adaptação para buscar mais de um arquivo conforme código abaixo. O único problema é que compilei em uma rpo de teste que tenho aqui e na produção, no rpo de teste, ele cria os pdfs certinho, quando rodo no rpo da produção ele gera um arquivo de 1k.
    Se eu pegar o rpo da produção e jogar na teste, ele funciona normal, se eu voltar para a produção ele gera arquivo de 1k, já deixei o appserver.ini iguais, já coloquei no mesmo balance e nada. Não sei o que pode ser….

    User Function gdanfe()
    Local aArea := GetArea()
    Local nRegs1 := 0
    Local cQuery := “”
    Local aItens := {}
    If AskMe() //função que faz as perguntas

    cQuery := “select F2_DOC, F2_SERIE, F2_CLIENTE ”
    cQuery += ” from ” +RetSqlName(“SF2″)
    cQuery += ” where F2_CLIENTE between ‘”+ AllTrim(MV_PAR02) +”‘ AND ‘”+ AllTrim(MV_PAR03) +”‘”
    cQuery += ” and F2_EMISSAO between ‘”+ DTOS(MV_PAR04) +”‘ AND ‘”+ DTOS(MV_PAR05) +”‘”
    cQuery += ” and F2_FILIAL ='” +xFilial(“SF2”)+ “‘ ”
    cQuery += ” and R_E_C_D_E_L_=0 ”
    cQuery := ChangeQuery(cQuery)
    TCQUERY cQuery New Alias “QRY”
    Count To nRegs1
    dbSelectArea(“QRY”)
    QRY->(dbGoTop())
    If nRegs1 > 0
    While ! QRY->(EoF())
    aAdd(aItens,{AllTrim(QRY->F2_DOC),AllTrim(QRY->F2_SERIE),AllTrim(MV_PAR01),AllTrim(QRY->F2_CLIENTE) })
    QRY->(DbSkip())
    EndDo
    Else
    MsgInfo(“Notas não encontradas!”,”Observação!”)
    EndIf
    EndIf
    If len(aItens) > 0
    xPrinD(aItens)
    aItens := {}
    EndIf
    If Select(“QRY”) > 0
    QRY->(dbCloseArea())
    EndIf
    RestArea(aArea)
    Return

    Static Function xPrinD(aPar)
    Local I := 0
    Local cDir := ” ”
    For I := 1 To Len(aPar)
    u_zGerDanfe(cValToChar(aPar[I][1]),cValToChar(aPar[I][2]),cValToChar(aPar[I][3]),cValToChar(aPar[I][4]))
    cDir := cValToChar(aPar[1][3])
    Next
    MsgInfo(“Notas salvas com sucesso, salvas em:”+cDir,”Observação!”)
    Return

  18. Elson disse:

    Pessoal. Quem tiver apresentando problema reportado acima. Pode ser o parâmetro MV_PAR07 e MV_PAR08 que está preenchido no seu perfil. coloquei a data vazia e deu tudo certo.

    MV_PAR01 := PadR(cNota, nTamNota) //Nota Inicial
    MV_PAR02 := PadR(cNota, nTamNota) //Nota Final
    MV_PAR03 := PadR(cSerie, nTamSerie) //Série da Nota
    MV_PAR04 := 2 //NF de Saida
    MV_PAR05 := 2 //Frente e Verso = Sim
    MV_PAR06 := 2 //DANFE simplificado = Nao
    MV_PAR07 := CTOD(” / / “)
    MV_PAR08 := CTOD(” / / “)

  19. Francisco c Dantas disse:

    Amigo DAN_ATILIO,

    Só para sinalizar, a classe FWMSPrinter não funciona com gravação de arquivos em pasta que não tenha uma letra (C:\, F:\ etc.)
    Exemplo:
    cArquivo:= “Meu_Arquivo.PDF”
    oDanfe := FWMSPrinter():New(cArquivo, IMP_PDF, .F., ”, .T., .F., , , .T., .T., , .F.)
    oDanfe:cPathPDF := “\x_relatorios\”

    Dá um erro que precisa ter uma Letra de Drive.

    Eu não consegui gerar a DANFE em PDF via schedule, por essa razão.

    ABs.

  20. GILDESIO CAMPOS disse:

    Olá pessoal. Interessante seu comentário, Francisco. Vou tentar essa solução. Obrigado.

  21. GERALDO disse:

    Bom dia to usando SIGAMDI

  22. Gabriel Helfenstens Aguiar disse:

    Boa tarde! Isso funciona sem que a nota seja transmitida ?

  23. Julio Storino disse:

    Olá, muito boa essa rotina.. mas estou com um problema, o XML que é baixado, parece não possuir a tag xMotivo, possuo uma integração com um sistema de geração de guias e estão pedindo o XML com esta tag, como posso extrair este XML ? pode me dar alguma dica.

    Obrigado.

    • Dan_Atilio disse:

      Boa tarde Julio.
      Obrigado pelo feedback. Bom, essa rotina utiliza funções padrões, então o ideal é você abrir o Protheus, ir em Faturamento, abrir o Nfe Sefaz, clicar em Outras Ações > Monitor, digitar um número de uma NF.
      Depois clique em Schema, e veja se no XML tem o conteúdo, se não tiver, ai você precisar o motivo de não estar gerando essa tag internamente no seu ambiente.

  24. Fernando disse:

    Olá. Infelizmente nada do que façamos resolve o problema do arquivo com 1k. Mas agradeço por disponibilizar a rotina.

  25. Lucas L. Lopez disse:

    Muito boa tarde!

    E pra gerar somente o Pdf da Danfe em uma pasta? Eu ignoro a função zSpedXML ? Ou mesmo assim irei precisar dela ?

  26. Lucas L Lopez disse:

    Muito Obrigado!!!

    Tenha uma ótima semana.

  27. Eriberto Berlt disse:

    Muito obrigado!
    Me ajudou muito esse Update de 2022, estava com esse problema e os dois parâmetros de Data corrigiram

    Tenha uma ótima semana.

  28. João Leão disse:

    Essa atualização de 2022 me ajudou muito e sempre recomendo seu site quando alguém me pergunta onde aprender ADVPL.
    Muito obrigado, seus conteúdos ajudam demais.

  29. josé macedo disse:

    Oi boa tarde, o meu ta gerando a danfe.rel e não pdf …. o que pode ser ?

  30. Cesar Sampaio disse:

    Bom dia, tudo bem ?
    Como faço pra gerar o pdf dentro de uma pasta da protheus_data ?

    • Boa tarde Cesar, tudo joia graças a Deus e você?

      Opa, recentemente nos perguntaram sobre isso no grupo do WhatsApp, segue a resposta que demos na ocasião:
      […]
      Para gerar então a DANFE via WebService ou um Scheduler, algumas mudanças precisam ser feitas no zGerDanfe, sendo:
      a. Você precisa ter o printer.exe dentro da pasta do appserver em questão
      b. A cPasta tem que apontar para um diretório dentro da Protheus Data, exemplo: cPasta := “\x_sua_pasta\”
      c. O atributo lServer tem que ser verdadeiro, exemplo: oDanfe:lServer := .T.

      Com isso, ao acionar o fonte na sua customização, ele vai gerar o arquivo dentro da Protheus Data, ai você pode disparar por email o pdf ou retornar ele em um WebService.
      […]

      Um grande abraço.

  31. Marcelo Lopes disse:

    Eu queria contrubuir , porque apanhei bastante para gerar o PDF no Protheus_data. Segue a solução :

    cPasta := “\pdfs\” // diretorio destino
    oDanfe := FWMSPrinter():New(cArquivo,6,.T., cPasta,.T.)
    oDanfe:cPathPDF:=cPasta
    oDanfe:SetViewPDF(.F.)

    //Propriedades da DANFE
    oDanfe:SetResolution(78)
    oDanfe:SetPortrait()
    oDanfe:SetPaperSize(DMPAPER_A4)
    oDanfe:SetMargin(60, 60, 60, 60)

Deixe uma resposta

Terminal de Informação