FWMSPrinter

Gera relatório de forma gráfica em AdvPL

Teste de Relatório

Exemplo da Rotina:

oPrintPvt := FWMSPrinter():New(cArquivo, IMP_PDF, .F., "", .T., , @oPrintPvt, "", , , , .T.)

Exemplo 1- Gerando relatório de forma manual:

//Bibliotecas
#Include "Protheus.ch"
#Include "TopConn.ch"
#Include "RPTDef.ch"
#Include "FWPrintSetup.ch"

//Alinhamentos
#Define PAD_LEFT    0
#Define PAD_RIGHT   1
#Define PAD_CENTER  2

//Cores
#Define COR_CINZA   RGB(180, 180, 180)
#Define COR_PRETO   RGB(000, 000, 000)

//Colunas
#Define COL_GRUPO   0015
#Define COL_DESCR   0095

/*/{Protheus.doc} zTstRel
Exemplos de FWMSPrinter
@author Atilio
@since 27/01/2019
@version 1.0
@type function
/*/

User Function zTstRel()
	Local aArea := GetArea()
	
	//Se a pergunta for confirmada
	If MsgYesNo("Deseja gerar o relatório de grupos de produtos?", "Atenção")
		Processa({|| fMontaRel()}, "Processando...")
	EndIf
	
	RestArea(aArea)
Return

/*---------------------------------------------------------------------*
 | Func:  fMontaRel                                                    |
 | Desc:  Função que monta o relatório                                 |
 *---------------------------------------------------------------------*/

Static Function fMontaRel()
	Local cCaminho    := ""
	Local cArquivo    := ""
	Local cQryAux     := ""
	Local nAtual      := 0
	Local nTotal      := 0
	//Linhas e colunas
	Private nLinAtu   := 000
	Private nTamLin   := 010
	Private nLinFin   := 820
	Private nColIni   := 010
	Private nColFin   := 550
	Private nColMeio  := (nColFin-nColIni)/2
	//Objeto de Impressão
	Private oPrintPvt
	//Variáveis auxiliares
	Private dDataGer  := Date()
	Private cHoraGer  := Time()
	Private nPagAtu   := 1
	Private cNomeUsr  := UsrRetName(RetCodUsr())
	//Fontes
	Private cNomeFont := "Arial"
	Private oFontDet  := TFont():New(cNomeFont, 9, -10, .T., .F., 5, .T., 5, .T., .F.)
	Private oFontDetN := TFont():New(cNomeFont, 9, -10, .T., .T., 5, .T., 5, .T., .F.)
	Private oFontRod  := TFont():New(cNomeFont, 9, -08, .T., .F., 5, .T., 5, .T., .F.)
	Private oFontTit  := TFont():New(cNomeFont, 9, -13, .T., .T., 5, .T., 5, .T., .F.)
	
	//Definindo o diretório como a temporária do S.O. e o nome do arquivo com a data e hora (sem dois pontos)
	cCaminho  := GetTempPath()
	cArquivo  := "zTstRel_" + dToS(dDataGer) + "_" + StrTran(cHoraGer, ':', '-')
	
	//Criando o objeto do FMSPrinter
	oPrintPvt := FWMSPrinter():New(cArquivo, IMP_PDF, .F., "", .T., , @oPrintPvt, "", , , , .T.)
	
	//Setando os atributos necessários do relatório
	oPrintPvt:SetResolution(72)
	oPrintPvt:SetPortrait()
	oPrintPvt:SetPaperSize(DMPAPER_A4)
	oPrintPvt:SetMargin(60, 60, 60, 60)
	
	//Imprime o cabeçalho
	fImpCab()
	
	//Montando a consulta
	cQryAux := " SELECT "                                       + CRLF
	cQryAux += "     BM_GRUPO, "                                + CRLF
	cQryAux += "     BM_DESC "                                  + CRLF
	cQryAux += " FROM "                                         + CRLF
	cQryAux += "     " + RetSQLName('SBM') + " SBM "            + CRLF
	cQryAux += " WHERE "                                        + CRLF
	cQryAux += "     BM_FILIAL = '" + FWxFilial('SBM') + "' "   + CRLF
	cQryAux += "     AND SBM.D_E_L_E_T_ = ' ' "                 + CRLF
	cQryAux += " ORDER BY "                                     + CRLF
	cQryAux += "     BM_GRUPO "                                 + CRLF
	TCQuery cQryAux New Alias "QRY_SBM"
	
	//Conta o total de registros, seta o tamanho da régua, e volta pro topo
	Count To nTotal
	ProcRegua(nTotal)
	QRY_SBM->(DbGoTop())
	nAtual := 0
	
	//Enquanto houver registros
	While ! QRY_SBM->(EoF())
		nAtual++
		IncProc("Imprimindo grupo " + QRY_SBM->BM_GRUPO + " (" + cValToChar(nAtual) + " de " + cValToChar(nTotal) + ")...")
		
		//Se a linha atual mais o espaço que será utilizado forem maior que a linha final, imprime rodapé e cabeçalho
		If nLinAtu + nTamLin > nLinFin
			fImpRod()
			fImpCab()
		EndIf
		
		//Imprimindo a linha atual
		oPrintPvt:SayAlign(nLinAtu, COL_GRUPO, QRY_SBM->BM_GRUPO, oFontDet, 0080, nTamLin, COR_PRETO, PAD_LEFT, 0)
		oPrintPvt:SayAlign(nLinAtu, COL_DESCR, QRY_SBM->BM_DESC,  oFontDet, 0200, nTamLin, COR_PRETO, PAD_LEFT, 0)
		nLinAtu += nTamLin
		
		QRY_SBM->(DbSkip())
	EndDo
	QRY_SBM->(DbCloseArea())
	
	//Se ainda tiver linhas sobrando na página, imprime o rodapé final
	If nLinAtu <= nLinFin
		fImpRod()
	EndIf
	
	//Mostrando o relatório
	oPrintPvt:Preview()
Return

/*---------------------------------------------------------------------*
 | Func:  fImpCab                                                      |
 | Desc:  Função que imprime o cabeçalho                               |
 *---------------------------------------------------------------------*/

Static Function fImpCab()
	Local cTexto   := ""
	Local nLinCab  := 030
	
	//Iniciando Página
	oPrintPvt:StartPage()
	
	//Cabeçalho
	cTexto := "Relação de Grupos de Produtos"
	oPrintPvt:SayAlign(nLinCab, nColMeio - 120, cTexto, oFontTit, 240, 20, COR_CINZA, PAD_CENTER, 0)
	
	//Linha Separatória
	nLinCab += (nTamLin * 2)
	oPrintPvt:Line(nLinCab, nColIni, nLinCab, nColFin, COR_CINZA)
	
	//Cabeçalho das colunas
	nLinCab += nTamLin
	oPrintPvt:SayAlign(nLinCab, COL_GRUPO, "Grupo",     oFontDetN, 0080, nTamLin, COR_PRETO, PAD_LEFT, 0)
	oPrintPvt:SayAlign(nLinCab, COL_DESCR, "Descrição", oFontDetN, 0200, nTamLin, COR_PRETO, PAD_LEFT, 0)
	nLinCab += nTamLin
	
	//Atualizando a linha inicial do relatório
	nLinAtu := nLinCab + 3
Return

/*---------------------------------------------------------------------*
 | Func:  fImpRod                                                      |
 | Desc:  Função que imprime o rodapé                                  |
 *---------------------------------------------------------------------*/

Static Function fImpRod()
	Local nLinRod   := nLinFin + nTamLin
	Local cTextoEsq := ''
	Local cTextoDir := ''

	//Linha Separatória
	oPrintPvt:Line(nLinRod, nColIni, nLinRod, nColFin, COR_CINZA)
	nLinRod += 3
	
	//Dados da Esquerda e Direita
	cTextoEsq := dToC(dDataGer) + "    " + cHoraGer + "    " + FunName() + "    " + cNomeUsr
	cTextoDir := "Página " + cValToChar(nPagAtu)
	
	//Imprimindo os textos
	oPrintPvt:SayAlign(nLinRod, nColIni,    cTextoEsq, oFontRod, 200, 05, COR_CINZA, PAD_LEFT,  0)
	oPrintPvt:SayAlign(nLinRod, nColFin-40, cTextoDir, oFontRod, 040, 05, COR_CINZA, PAD_RIGHT, 0)
	
	//Finalizando a página e somando mais um
	oPrintPvt:EndPage()
	nPagAtu++
Return

Exemplo 2- Relatório com opção de forma manual e via job:

//Bibliotecas
#Include "Protheus.ch"
#Include "TopConn.ch"
#Include "RPTDef.ch"
#Include "FWPrintSetup.ch"

//Alinhamentos
#Define PAD_LEFT    0
#Define PAD_RIGHT   1
#Define PAD_CENTER  2

//Cores
#Define COR_CINZA   RGB(180, 180, 180)
#Define COR_PRETO   RGB(000, 000, 000)

//Colunas
#Define COL_GRUPO   0015
#Define COL_DESCR   0095

/*/{Protheus.doc} zTstRel
Exemplos de FWMSPrinter
@author Atilio
@since 27/01/2019
@version 1.0
@type function
/*/

User Function zTstRel()
	Local aArea  := GetArea()
	Private lJob := .F.
	
	//Chamado pelo Schedule
	If Select("SX2") == 0
		//Preparando o ambiente
		lJob := .T.
		RPCSetType(3)
		RPCSetEnv("01", "01", "", "", "")
	EndIf
	
	//Se for execução automática, não mostra pergunta, executa direto
	If lJob
		Processa({|| fMontaRel()}, "Processando...")
		
	//Senão, se a pergunta for confirmada, executa o relatório
	Else
		If MsgYesNo("Deseja gerar o relatório de grupos de produtos?", "Atenção")
			Processa({|| fMontaRel()}, "Processando...")
		EndIf
	EndIf
	
	RestArea(aArea)
Return

/*---------------------------------------------------------------------*
 | Func:  fMontaRel                                                    |
 | Desc:  Função que monta o relatório                                 |
 *---------------------------------------------------------------------*/

Static Function fMontaRel()
	Local cCaminho    := ""
	Local cArquivo    := ""
	Local cQryAux     := ""
	Local nAtual      := 0
	Local nTotal      := 0
	//Linhas e colunas
	Private nLinAtu   := 000
	Private nTamLin   := 010
	Private nLinFin   := 820
	Private nColIni   := 010
	Private nColFin   := 550
	Private nColMeio  := (nColFin-nColIni)/2
	//Objeto de Impressão
	Private oPrintPvt
	//Variáveis auxiliares
	Private dDataGer  := Date()
	Private cHoraGer  := Time()
	Private nPagAtu   := 1
	Private cNomeUsr  := UsrRetName(RetCodUsr())
	//Fontes
	Private cNomeFont := "Arial"
	Private oFontDet  := TFont():New(cNomeFont, 9, -10, .T., .F., 5, .T., 5, .T., .F.)
	Private oFontDetN := TFont():New(cNomeFont, 9, -10, .T., .T., 5, .T., 5, .T., .F.)
	Private oFontRod  := TFont():New(cNomeFont, 9, -08, .T., .F., 5, .T., 5, .T., .F.)
	Private oFontTit  := TFont():New(cNomeFont, 9, -13, .T., .T., 5, .T., 5, .T., .F.)
	
	//Se for via JOB, muda as parametrizações
	If lJob
		//Define o caminho dentro da protheus data e o nome do arquivo
		cCaminho := "\x_relatorios\"
		cArquivo := "zTstRel_job_" + dToS(dDataGer) + "_" + StrTran(cHoraGer, ':', '-') + ".pdf"
		
		//Se não existir a pasta na Protheus Data, cria ela
		If ! ExistDir(cCaminho)
			MakeDir(cCaminho)
		EndIf
		
		//Cria o objeto FWMSPrinter
		oPrintPvt := FWMSPrinter():New(cArquivo, IMP_PDF, .F., '', .T., .F., , , .T., .T., , .F.)
		oPrintPvt:cPathPDF := cCaminho
		
	Else
		//Definindo o diretório como a temporária do S.O. e o nome do arquivo com a data e hora (sem dois pontos)
		cCaminho  := GetTempPath()
		cArquivo  := "zTstRel_" + dToS(dDataGer) + "_" + StrTran(cHoraGer, ':', '-')
		
		//Criando o objeto do FMSPrinter
		oPrintPvt := FWMSPrinter():New(cArquivo, IMP_PDF, .F., "", .T., , @oPrintPvt, "", , , , .T.)
	EndIf
	
	//Setando os atributos necessários do relatório
	oPrintPvt:SetResolution(72)
	oPrintPvt:SetPortrait()
	oPrintPvt:SetPaperSize(DMPAPER_A4)
	oPrintPvt:SetMargin(60, 60, 60, 60)
	
	//Imprime o cabeçalho
	fImpCab()
	
	//Montando a consulta
	cQryAux := " SELECT "                                       + CRLF
	cQryAux += "     BM_GRUPO, "                                + CRLF
	cQryAux += "     BM_DESC "                                  + CRLF
	cQryAux += " FROM "                                         + CRLF
	cQryAux += "     " + RetSQLName('SBM') + " SBM "            + CRLF
	cQryAux += " WHERE "                                        + CRLF
	cQryAux += "     BM_FILIAL = '" + FWxFilial('SBM') + "' "   + CRLF
	cQryAux += "     AND SBM.D_E_L_E_T_ = ' ' "                 + CRLF
	cQryAux += " ORDER BY "                                     + CRLF
	cQryAux += "     BM_GRUPO "                                 + CRLF
	TCQuery cQryAux New Alias "QRY_SBM"
	
	//Conta o total de registros, seta o tamanho da régua, e volta pro topo
	Count To nTotal
	ProcRegua(nTotal)
	QRY_SBM->(DbGoTop())
	nAtual := 0
	
	//Enquanto houver registros
	While ! QRY_SBM->(EoF())
		nAtual++
		IncProc("Imprimindo grupo " + QRY_SBM->BM_GRUPO + " (" + cValToChar(nAtual) + " de " + cValToChar(nTotal) + ")...")
		
		//Se a linha atual mais o espaço que será utilizado forem maior que a linha final, imprime rodapé e cabeçalho
		If nLinAtu + nTamLin > nLinFin
			fImpRod()
			fImpCab()
		EndIf
		
		//Imprimindo a linha atual
		oPrintPvt:SayAlign(nLinAtu, COL_GRUPO, QRY_SBM->BM_GRUPO, oFontDet, 0080, nTamLin, COR_PRETO, PAD_LEFT, 0)
		oPrintPvt:SayAlign(nLinAtu, COL_DESCR, QRY_SBM->BM_DESC,  oFontDet, 0200, nTamLin, COR_PRETO, PAD_LEFT, 0)
		nLinAtu += nTamLin
		
		QRY_SBM->(DbSkip())
	EndDo
	QRY_SBM->(DbCloseArea())
	
	//Se ainda tiver linhas sobrando na página, imprime o rodapé final
	If nLinAtu <= nLinFin
		fImpRod()
	EndIf
	
	//Se for via job, imprime o arquivo para gerar corretamente o pdf
	If lJob
		oPrintPvt:Print()
		
	//Se for via manual, mostra o relatório
	Else
		oPrintPvt:Preview()
	EndIf
Return

/*---------------------------------------------------------------------*
 | Func:  fImpCab                                                      |
 | Desc:  Função que imprime o cabeçalho                               |
 *---------------------------------------------------------------------*/

Static Function fImpCab()
	Local cTexto   := ""
	Local nLinCab  := 030
	
	//Iniciando Página
	oPrintPvt:StartPage()
	
	//Cabeçalho
	cTexto := "Relação de Grupos de Produtos"
	oPrintPvt:SayAlign(nLinCab, nColMeio - 120, cTexto, oFontTit, 240, 20, COR_CINZA, PAD_CENTER, 0)
	
	//Linha Separatória
	nLinCab += (nTamLin * 2)
	oPrintPvt:Line(nLinCab, nColIni, nLinCab, nColFin, COR_CINZA)
	
	//Cabeçalho das colunas
	nLinCab += nTamLin
	oPrintPvt:SayAlign(nLinCab, COL_GRUPO, "Grupo",     oFontDetN, 0080, nTamLin, COR_PRETO, PAD_LEFT, 0)
	oPrintPvt:SayAlign(nLinCab, COL_DESCR, "Descrição", oFontDetN, 0200, nTamLin, COR_PRETO, PAD_LEFT, 0)
	nLinCab += nTamLin
	
	//Atualizando a linha inicial do relatório
	nLinAtu := nLinCab + 3
Return

/*---------------------------------------------------------------------*
 | Func:  fImpRod                                                      |
 | Desc:  Função que imprime o rodapé                                  |
 *---------------------------------------------------------------------*/

Static Function fImpRod()
	Local nLinRod   := nLinFin + nTamLin
	Local cTextoEsq := ''
	Local cTextoDir := ''

	//Linha Separatória
	oPrintPvt:Line(nLinRod, nColIni, nLinRod, nColFin, COR_CINZA)
	nLinRod += 3
	
	//Dados da Esquerda e Direita
	cTextoEsq := dToC(dDataGer) + "    " + cHoraGer + "    " + FunName() + "    " + cNomeUsr
	cTextoDir := "Página " + cValToChar(nPagAtu)
	
	//Imprimindo os textos
	oPrintPvt:SayAlign(nLinRod, nColIni,    cTextoEsq, oFontRod, 200, 05, COR_CINZA, PAD_LEFT,  0)
	oPrintPvt:SayAlign(nLinRod, nColFin-40, cTextoDir, oFontRod, 040, 05, COR_CINZA, PAD_RIGHT, 0)
	
	//Finalizando a página e somando mais um
	oPrintPvt:EndPage()
	nPagAtu++
Return

Exemplo 3- Relatório com opção de forma manual e via job com disparo de e-Mail:

//Bibliotecas
#Include "Protheus.ch"
#Include "TopConn.ch"
#Include "RPTDef.ch"
#Include "FWPrintSetup.ch"

//Alinhamentos
#Define PAD_LEFT    0
#Define PAD_RIGHT   1
#Define PAD_CENTER  2

//Cores
#Define COR_CINZA   RGB(180, 180, 180)
#Define COR_PRETO   RGB(000, 000, 000)

//Colunas
#Define COL_GRUPO   0015
#Define COL_DESCR   0095

/*/{Protheus.doc} zTstRel
Exemplos de FWMSPrinter
@author Atilio
@since 27/01/2019
@version 1.0
@type function
/*/

User Function zTstRel()
	Local aArea  := GetArea()
	Private lJob := .F.
	
	//Chamado pelo Schedule
	If Select("SX2") == 0
		//Preparando o ambiente
		lJob := .T.
		RPCSetType(3)
		RPCSetEnv("01", "01", "", "", "")
	EndIf
	
	//Se for execução automática, não mostra pergunta, executa direto
	If lJob
		Processa({|| fMontaRel()}, "Processando...")
		
	//Senão, se a pergunta for confirmada, executa o relatório
	Else
		If MsgYesNo("Deseja gerar o relatório de grupos de produtos?", "Atenção")
			Processa({|| fMontaRel()}, "Processando...")
		EndIf
	EndIf
	
	RestArea(aArea)
Return

/*---------------------------------------------------------------------*
 | Func:  fMontaRel                                                    |
 | Desc:  Função que monta o relatório                                 |
 *---------------------------------------------------------------------*/

Static Function fMontaRel()
	Local cCaminho    := ""
	Local cArquivo    := ""
	Local cQryAux     := ""
	Local nAtual      := 0
	Local nTotal      := 0
	//Variáveis para disparo de e-Mail
	Local cPara    := ""
	Local cAssunto := ""
	Local cCorpo   := ""
	Local aAnexos  := {}
	//Linhas e colunas
	Private nLinAtu   := 000
	Private nTamLin   := 010
	Private nLinFin   := 820
	Private nColIni   := 010
	Private nColFin   := 550
	Private nColMeio  := (nColFin-nColIni)/2
	//Objeto de Impressão
	Private oPrintPvt
	//Variáveis auxiliares
	Private dDataGer  := Date()
	Private cHoraGer  := Time()
	Private nPagAtu   := 1
	Private cNomeUsr  := UsrRetName(RetCodUsr())
	//Fontes
	Private cNomeFont := "Arial"
	Private oFontDet  := TFont():New(cNomeFont, 9, -10, .T., .F., 5, .T., 5, .T., .F.)
	Private oFontDetN := TFont():New(cNomeFont, 9, -10, .T., .T., 5, .T., 5, .T., .F.)
	Private oFontRod  := TFont():New(cNomeFont, 9, -08, .T., .F., 5, .T., 5, .T., .F.)
	Private oFontTit  := TFont():New(cNomeFont, 9, -13, .T., .T., 5, .T., 5, .T., .F.)
	
	//Se for via JOB, muda as parametrizações
	If lJob
		//Define o caminho dentro da protheus data e o nome do arquivo
		cCaminho := "\x_relatorios\"
		cArquivo := "zTstRel_job_" + dToS(dDataGer) + "_" + StrTran(cHoraGer, ':', '-') + ".pdf"
		
		//Se não existir a pasta na Protheus Data, cria ela
		If ! ExistDir(cCaminho)
			MakeDir(cCaminho)
		EndIf
		
		//Cria o objeto FWMSPrinter
		oPrintPvt := FWMSPrinter():New(cArquivo, IMP_PDF, .F., '', .T., .F., , , .T., .T., , .F.)
		oPrintPvt:cPathPDF := cCaminho
		
	Else
		//Definindo o diretório como a temporária do S.O. e o nome do arquivo com a data e hora (sem dois pontos)
		cCaminho  := GetTempPath()
		cArquivo  := "zTstRel_" + dToS(dDataGer) + "_" + StrTran(cHoraGer, ':', '-')
		
		//Criando o objeto do FMSPrinter
		oPrintPvt := FWMSPrinter():New(cArquivo, IMP_PDF, .F., "", .T., , @oPrintPvt, "", , , , .T.)
	EndIf
	
	//Setando os atributos necessários do relatório
	oPrintPvt:SetResolution(72)
	oPrintPvt:SetPortrait()
	oPrintPvt:SetPaperSize(DMPAPER_A4)
	oPrintPvt:SetMargin(60, 60, 60, 60)
	
	//Imprime o cabeçalho
	fImpCab()
	
	//Montando a consulta
	cQryAux := " SELECT "                                       + CRLF
	cQryAux += "     BM_GRUPO, "                                + CRLF
	cQryAux += "     BM_DESC "                                  + CRLF
	cQryAux += " FROM "                                         + CRLF
	cQryAux += "     " + RetSQLName('SBM') + " SBM "            + CRLF
	cQryAux += " WHERE "                                        + CRLF
	cQryAux += "     BM_FILIAL = '" + FWxFilial('SBM') + "' "   + CRLF
	cQryAux += "     AND SBM.D_E_L_E_T_ = ' ' "                 + CRLF
	cQryAux += " ORDER BY "                                     + CRLF
	cQryAux += "     BM_GRUPO "                                 + CRLF
	TCQuery cQryAux New Alias "QRY_SBM"
	
	//Conta o total de registros, seta o tamanho da régua, e volta pro topo
	Count To nTotal
	ProcRegua(nTotal)
	QRY_SBM->(DbGoTop())
	nAtual := 0
	
	//Enquanto houver registros
	While ! QRY_SBM->(EoF())
		nAtual++
		IncProc("Imprimindo grupo " + QRY_SBM->BM_GRUPO + " (" + cValToChar(nAtual) + " de " + cValToChar(nTotal) + ")...")
		
		//Se a linha atual mais o espaço que será utilizado forem maior que a linha final, imprime rodapé e cabeçalho
		If nLinAtu + nTamLin > nLinFin
			fImpRod()
			fImpCab()
		EndIf
		
		//Imprimindo a linha atual
		oPrintPvt:SayAlign(nLinAtu, COL_GRUPO, QRY_SBM->BM_GRUPO, oFontDet, 0080, nTamLin, COR_PRETO, PAD_LEFT, 0)
		oPrintPvt:SayAlign(nLinAtu, COL_DESCR, QRY_SBM->BM_DESC,  oFontDet, 0200, nTamLin, COR_PRETO, PAD_LEFT, 0)
		nLinAtu += nTamLin
		
		QRY_SBM->(DbSkip())
	EndDo
	QRY_SBM->(DbCloseArea())
	
	//Se ainda tiver linhas sobrando na página, imprime o rodapé final
	If nLinAtu <= nLinFin
		fImpRod()
	EndIf
	
	//Se for via job, imprime o arquivo para gerar corretamente o pdf
	If lJob
		oPrintPvt:Print()
		
		//Atenção! - é necessário baixar a função zEnvMail() - disponível em https://terminaldeinformacao.com/2017/10/17/funcao-dispara-e-mail-varios-anexos-em-advpl/
		cPara    := "teste@teste.com"
		cAssunto := "Assunto Teste"
		cCorpo   := "Corpo do e-Mail Teste"
		aAdd(aAnexos, cCaminho + cArquivo)
		u_zEnvMail(cPara, cAssunto, cCorpo, aAnexos)
		
	//Se for via manual, mostra o relatório
	Else
		oPrintPvt:Preview()
	EndIf
Return

/*---------------------------------------------------------------------*
 | Func:  fImpCab                                                      |
 | Desc:  Função que imprime o cabeçalho                               |
 *---------------------------------------------------------------------*/

Static Function fImpCab()
	Local cTexto   := ""
	Local nLinCab  := 030
	
	//Iniciando Página
	oPrintPvt:StartPage()
	
	//Cabeçalho
	cTexto := "Relação de Grupos de Produtos"
	oPrintPvt:SayAlign(nLinCab, nColMeio - 120, cTexto, oFontTit, 240, 20, COR_CINZA, PAD_CENTER, 0)
	
	//Linha Separatória
	nLinCab += (nTamLin * 2)
	oPrintPvt:Line(nLinCab, nColIni, nLinCab, nColFin, COR_CINZA)
	
	//Cabeçalho das colunas
	nLinCab += nTamLin
	oPrintPvt:SayAlign(nLinCab, COL_GRUPO, "Grupo",     oFontDetN, 0080, nTamLin, COR_PRETO, PAD_LEFT, 0)
	oPrintPvt:SayAlign(nLinCab, COL_DESCR, "Descrição", oFontDetN, 0200, nTamLin, COR_PRETO, PAD_LEFT, 0)
	nLinCab += nTamLin
	
	//Atualizando a linha inicial do relatório
	nLinAtu := nLinCab + 3
Return

/*---------------------------------------------------------------------*
 | Func:  fImpRod                                                      |
 | Desc:  Função que imprime o rodapé                                  |
 *---------------------------------------------------------------------*/

Static Function fImpRod()
	Local nLinRod   := nLinFin + nTamLin
	Local cTextoEsq := ''
	Local cTextoDir := ''

	//Linha Separatória
	oPrintPvt:Line(nLinRod, nColIni, nLinRod, nColFin, COR_CINZA)
	nLinRod += 3
	
	//Dados da Esquerda e Direita
	cTextoEsq := dToC(dDataGer) + "    " + cHoraGer + "    " + FunName() + "    " + cNomeUsr
	cTextoDir := "Página " + cValToChar(nPagAtu)
	
	//Imprimindo os textos
	oPrintPvt:SayAlign(nLinRod, nColIni,    cTextoEsq, oFontRod, 200, 05, COR_CINZA, PAD_LEFT,  0)
	oPrintPvt:SayAlign(nLinRod, nColFin-40, cTextoDir, oFontRod, 040, 05, COR_CINZA, PAD_RIGHT, 0)
	
	//Finalizando a página e somando mais um
	oPrintPvt:EndPage()
	nPagAtu++
Return

Observações:

– Caso tenha dúvidas ou problemas com os exemplos, entre em contato;

– Se tiver sugestões de rotinas, pode entrar em contato;

Referências:

TDN