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]
	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
		
		//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| StaticCall(DANFEII, 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.

Bom pessoal, por hoje é só.

Abraços e até a próxima.

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

44 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. 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.

Deixe uma resposta