Criando relatórios com TReport, TRFunction, TRSection e TRCell – Maratona AdvPL e TL++ 505

Nesse vídeo demonstraremos a utilização das classes TReport, TRFunction, TRSection e TRCell, que servem para criar um relatório com opções de imprimir, gerar PDF, gerar Excel, entre outras.

Abaixo o código fonte desenvolvido para o exemplo em vídeo acima:

//Bibliotecas
#Include "TOTVS.ch"

/*/{Protheus.doc} User Function zExe505
Classes para montagem de um relatório com listagem de informações
@type Function
@author Atilio
@since 04/04/2023
@see https://tdn.totvs.com/display/public/framework/TReport e https://tdn.totvs.com/display/public/framework/TRSection e https://tdn.totvs.com/display/public/framework/TRFunction e https://tdn.totvs.com/display/public/framework/TRCell
@obs 

    **** Apoie nosso projeto, se inscreva em https://www.youtube.com/TerminalDeInformacao ****
/*/

User Function zExe505()
    Local aArea := FWGetArea()
	Local oReport
	Local aPergs   := {}
	Local cProdDe  := Space(TamSX3('B1_COD')[1])
	Local cProdAte := StrTran(cProdDe, ' ', 'Z')
	Local cTipoDe  := Space(TamSX3('B1_TIPO')[1])
	Local cTipoAte := StrTran(cTipoDe, ' ', 'Z')
	Local nOrden   := 1
	
	//Adicionando os parametros do ParamBox
	aAdd(aPergs, {1, "Produto De",  cProdDe,  "", ".T.", "SB1", ".T.", 80,  .F.}) // MV_PAR01
	aAdd(aPergs, {1, "Produto Até", cProdAte, "", ".T.", "SB1", ".T.", 80,  .T.}) // MV_PAR02
	aAdd(aPergs, {1, "Tipo De",     cTipoDe,  "", ".T.", "02",  ".T.", 40,  .F.}) // MV_PAR03
	aAdd(aPergs, {1, "Tipo Até",    cTipoAte, "", ".T.", "02",  ".T.", 40,  .T.}) // MV_PAR04
    aAdd(aPergs, {2, "Ordenar por", nOrden, {"1=Código do Produto", "2=Descrição do Produto", "3=Unidade de Medida"},  100, ".T.", .T.}) // MV_PAR05
	
	//Se a pergunta for confirma, cria as definicoes do relatorio
	If ParamBox(aPergs, "Informe os parâmetros", , , , , , , , , .F., .F.)
        MV_PAR05 := Val(cValToChar(MV_PAR05))

		oReport := fReportDef()
		oReport:PrintDialog()
	EndIf
	
	FWRestArea(aArea)
Return

Static Function fReportDef()
	Local oReport
	Local oSection := Nil
	
	//Criacao do componente de impressao
	oReport := TReport():New( "zRel02",;
		"Relatório de Produto",;
		,;
		{|oReport| fRepPrint(oReport),};
		)
	oReport:SetTotalInLine(.F.)
	oReport:lParamPage := .F.
	oReport:oPage:SetPaperSize(9)
	
	//Orientacao do Relatorio
	oReport:SetPortrait()
	
	//Definicoes da fonte utilizada
	oReport:SetLineHeight(50)
	oReport:nFontBody := 12
	
	//Criando a secao de dados
	oSection := TRSection():New( oReport,;
		"Dados",;
		{"QRY_REP"})
	oSection:SetTotalInLine(.F.)
	
	//Colunas do relatorio
	TRCell():New(oSection, "B1_COD",    "QRY_REP", "Codigo",     /*cPicture*/, 15, /*lPixel*/, /*{|| code-block de impressao }*/, "LEFT", /*lLineBreak*/, "LEFT", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .T.)
	TRCell():New(oSection, "B1_DESC",   "QRY_REP", "Descricao",  /*cPicture*/, 30, /*lPixel*/, /*{|| code-block de impressao }*/, "LEFT", /*lLineBreak*/, "LEFT", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
	TRCell():New(oSection, "B1_TIPO",   "QRY_REP", "Tipo",       /*cPicture*/, 02, /*lPixel*/, /*{|| code-block de impressao }*/, "LEFT", /*lLineBreak*/, "LEFT", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
	TRCell():New(oSection, "TIPODESCR", "QRY_REP", "Tp. Descr.", /*cPicture*/, 55, /*lPixel*/, /*{|| code-block de impressao }*/, "LEFT", /*lLineBreak*/, "LEFT", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
	TRCell():New(oSection, "B1_UM",     "QRY_REP", "Unid. Med.", /*cPicture*/, 02, /*lPixel*/, /*{|| code-block de impressao }*/, "LEFT", /*lLineBreak*/, "LEFT", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
	TRCell():New(oSection, "UMDESCR",   "QRY_REP", "UM. Descr.", /*cPicture*/, 40, /*lPixel*/, /*{|| code-block de impressao }*/, "LEFT", /*lLineBreak*/, "LEFT", /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
	
    //Quebras do relatorio
	oBreak := TRBreak():New(oSection, oSection:Cell("B1_TIPO"), {||"Total da Quebra"}, .F.)

	//Totalizadores
	TRFunction():New(oSection:Cell("B1_COD"), , "COUNT", , , "@E 999,999,999", , .F.)
	
Return oReport

Static Function fRepPrint(oReport)
	Local aArea    := FWGetArea()
	Local cQryReport  := ""
	Local oSectDad := Nil
	Local nAtual   := 0
	Local nTotal   := 0
	
	//Pegando as secoes do relatorio
	oSectDad := oReport:Section(1)
	
	//Montando consulta de dados
	cQryReport += "SELECT "		+ CRLF
	cQryReport += "    B1_COD, "		+ CRLF
	cQryReport += "    B1_DESC, "		+ CRLF
	cQryReport += "    B1_TIPO, "		+ CRLF
	cQryReport += "    ISNULL(X5_DESCRI, '') AS TIPODESCR, "		+ CRLF
	cQryReport += "    B1_UM, "		+ CRLF
	cQryReport += "    ISNULL(AH_DESCPO, '') AS UMDESCR "		+ CRLF
	cQryReport += "FROM "		+ CRLF
	cQryReport += "    " + RetSQLName("SB1") + " SB1 "		+ CRLF
	cQryReport += "    LEFT JOIN " + RetSQLName("SX5") + " SX5 ON ( "		+ CRLF
	cQryReport += "       X5_FILIAL = '" + FWxFilial("SX5") + "' "		+ CRLF
	cQryReport += "       AND X5_TABELA = '02' "		+ CRLF
	cQryReport += "       AND X5_CHAVE = B1_TIPO "		+ CRLF
	cQryReport += "       AND SX5.D_E_L_E_T_ = ' ' "		+ CRLF
	cQryReport += "    ) "		+ CRLF
	cQryReport += "    LEFT JOIN " + RetSQLName("SAH") + " SAH ON ( "		+ CRLF
	cQryReport += "       AH_FILIAL = '" + FWxFilial("SAH") + "' "		+ CRLF
	cQryReport += "       AND AH_UNIMED = B1_UM "		+ CRLF
	cQryReport += "       AND SAH.D_E_L_E_T_ = ' ' "		+ CRLF
	cQryReport += "    ) "		+ CRLF
	cQryReport += "WHERE "		+ CRLF
	cQryReport += "    B1_FILIAL = '" + FWxFilial("SB1") + "' "		+ CRLF
	cQryReport += "    AND B1_COD >= '" + MV_PAR01 + "' "		+ CRLF
	cQryReport += "    AND B1_COD <= '" + MV_PAR02 + "' "		+ CRLF
	cQryReport += "    AND B1_TIPO >= '" + MV_PAR03 + "' "		+ CRLF
	cQryReport += "    AND B1_TIPO <= '" + MV_PAR04 + "' "		+ CRLF
	cQryReport += "    AND B1_MSBLQL != '1' "		+ CRLF
	cQryReport += "    AND SB1.D_E_L_E_T_ = ' ' "		+ CRLF
	cQryReport += "ORDER BY "		+ CRLF
    cQryReport += "    B1_TIPO, "		+ CRLF
    If MV_PAR05 == 1
	    cQryReport += "    B1_COD "		+ CRLF
    ElseIf MV_PAR05 == 2
        cQryReport += "    B1_DESC "		+ CRLF
    ElseIf MV_PAR05 == 3
        cQryReport += "    B1_UM "		+ CRLF
    EndIf
	
	//Executando consulta e setando o total da regua
	PlsQuery(cQryReport, "QRY_REP")
	DbSelectArea("QRY_REP")
	Count to nTotal
	oReport:SetMeter(nTotal)
	
	//Enquanto houver dados
	oSectDad:Init()
	QRY_REP->(DbGoTop())
	While ! QRY_REP->(Eof())
	
		//Incrementando a regua
		nAtual++
		oReport:SetMsgPrint("Imprimindo registro " + cValToChar(nAtual) + " de " + cValToChar(nTotal) + "...")
		oReport:IncMeter()
		
		//Imprimindo a linha atual
		oSectDad:PrintLine()
		
		QRY_REP->(DbSkip())
	EndDo
	oSectDad:Finish()
	QRY_REP->(DbCloseArea())
	
	FWRestArea(aArea)
Return

Bom pessoal, por hoje é só.

Abraços e até 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.

4 Responses

  1. MATHEUS HENRIQUE ALCANTARA DE FARIAS disse:

    eu to duvida no relatório treport e na secao2 eu coloco a mesma coisa que na seção5 mas só a 5 sai do jeito que eu quero

    Tipo aqui

    TRCell():New(oSection2, “C9_Pedido+C9_SEQUEN” ,””/Onde é buscado o campo */,”Numero O.F” /*Titulo/,/Picture/,20 /Tamanho/,/lPixel/,/{|| bloco-de-impressao }/, “LEFT” /Alinhamento/, .T./lLineBreak ou SetBlock/, “LEFT”, /lCellBreak/, /nColSpace/, /lAutoSize/, /nClrBack/, /nClrForce/, .T./*Negrito = T sem negrito = F */)

    TRCell():New(oSection5, “C9_XENTREG” ,””/Onde é buscado o campo */,”** Data Entrega” /Titulo/,/Picture/,20 /Tamanho/,/lPixel/,/{|| bloco-de-impressao }/, “LEFT” /Alinhamento/, .T./lLineBreak ou SetBlock/, “LEFT”, /lCellBreak/, /nColSpace/, /lAutoSize/, /nClrBack/, /nClrForce/, .T./*Negrito = T sem negrito = F */)

    • Bom dia Matheus, tudo joia?

      Eu não me lembro de ter visto nome composto em célula, por exemplo, isso aqui:

      TRCell():New(oSection2, "C9_Pedido+C9_SEQUEN" ...
      

      Ao invés disso, um teste que eu faria seria, colocar um nome:

      TRCell():New(oSection2, "PEDSEQUEN"
      

      E depois dentro do laço de repetição de impressão, antes do PrintLine, exemplo:

      oSuaSection:Cell("PEDSEQUEN"):SetValue(SC9->C9_Pedido + SC9->C9_SEQUEN)
      oSuaSection:PrintLine()
      

      Tenha um ótimo e abençoado fim de semana.

      Um forte abraço.

  2. Gabriel M. Viana disse:

    A quebra do oBreak não funciona em Excel? Aparentemente em modo Arquivo as quebras funcionam corretamente, mas em Formato de tabela, não funcionam nem as quebras nem os os dois TRFunctions
    Ou estou errando em algum momento?

    oSectionTotalizers := TRSection():New( oReport,;
    “Totalizadores”,;
    {“QRY_REP1”})
    oSectionTotalizers:SetTotalInLine(.F.)

    //Colunas de totalizadores
    TRCell():New(oSectionTotalizers, “D3_COD”, “QRY_REP1”, “Produto”, /*cPicture*/, 15, /*lPixel*/, /*{|| code-block de impressao }*/, “LEFT”, /*lLineBreak*/, “LEFT”, /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .T.)
    TRCell():New(oSectionTotalizers, “B1_DESC”, “QRY_REP1”, “Descrição”, /*cPicture*/, 40, /*lPixel*/, /*{|| code-block de impressao }*/, “LEFT”, /*lLineBreak*/, “LEFT”, /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
    TRCell():New(oSectionTotalizers, “D3_UM”, “QRY_REP1”, “Unid.”, /*cPicture*/, 05, /*lPixel*/, /*{|| code-block de impressao }*/, “LEFT”, /*lLineBreak*/, “LEFT”, /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .F.)
    TRCell():New(oSectionTotalizers, “AH_DESCPO”, “QRY_REP1”, “Descrição Unid.”, /*cPicture*/, 15, /*lPixel*/, /*{|| code-block de impressao }*/, “LEFT”, /*lLineBreak*/, “LEFT”, /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .T.)
    TRCell():New(oSectionTotalizers, “D3_QUANT”, “QRY_REP1”, “Quantidade”, /*cPicture*/, 15, /*lPixel*/, /*{|| code-block de impressao }*/, “RIGHT”, /*lLineBreak*/, “RIGHT”, /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .T.)
    TRCell():New(oSectionTotalizers, “D3_CUSTO1”, “QRY_REP1”, “Custo”, /*cPicture*/, 15, /*lPixel*/, /*{|| code-block de impressao }*/, “RIGHT”, /*lLineBreak*/, “RIGHT”, /*lCellBreak*/, /*nColSpace*/, /*lAutoSize*/, /*nClrBack*/, /*nClrFore*/, .T.)
    oBreak := TRBreak():New(oSectionTotalizers, oSectionTotalizers:Cell(“D3_COD”), {||”Total”}, .F. )
    TRFunction():New(oSectionTotalizers:Cell(“D3_CUSTO1″),”D3_CUSTO1″ ,”SUM”,oBreak,,PesqPict(“SD3”, “D3_CUSTO1”),,.T.,.F.) //Função de totalizador dentro do oBreak
    TRFunction():New(oSectionTotalizers:Cell(“D3_QUANT”),”D3_QUANT” ,”SUM”,oBreak,,PesqPict(“SD3”, “D3_QUANT”),,.T.,.F.)

    • Bom dia Gabriel, tudo joia?

      Até onde me lembro, no formato “Tabela” ele não funciona, pois o comportamento fica como se tivesse criando um relatório com FWMSExcel (que simplesmente exporta os dados uma linha abaixo da outra).

      Não sei dizer se tem alguma parametrização, ou se você precisaria customizar algo manualmente.

      Por exemplo, criar um FWPrinterXLSX e nele tratar as quebras e totalizadores.

      Tenha uma ótima e abençoada sexta feira.

      Um forte abraço.

Deixe uma resposta

Terminal de Informação