TReport com sub-secoes e totalizadores
Como criar relatorio TReport hierarquico — secao principal + sub-secao + totalizador automatico. Padrao de relatorio gerencial.
TReport e o framework moderno de relatorios. Sua maior virtude esta em sub-secoes hierarquicas — voce define uma secao principal (clientes) e sub-secoes (titulos do cliente), e o framework cuida da agregacao, totalizacao e quebras automaticas.
Estrutura tipica
- Section 1: cliente (linha principal)
- Section 2: titulos do cliente (linhas filhas)
- Quebra automatica por cliente
- Total parcial ao fim de cada cliente
- Total geral ao fim do relatorio
Codigo completo
#include "totvs.ch"
#include "totvs.ch"
#include "report.ch"
User Function RelTitCli()
Local oReport := TReport():New("RELTITCLI", ;
"Titulos em aberto por Cliente", ;
"MTRELTIT", ;
{|oRep| ImprimeRel(oRep)}, ;
"Lista de titulos a receber agrupado por cliente")
// Secao 1: Cliente
Local oSecCli := TRSection():New(oReport, "Cliente", {"SA1"})
oSecCli:SetTotalInLine(.F.)
TRCell():New(oSecCli, "A1_COD", "SA1", "Codigo")
TRCell():New(oSecCli, "A1_NOME", "SA1", "Nome")
TRCell():New(oSecCli, "A1_CGC", "SA1", "CNPJ", "@R 99.999.999/9999-99")
// Sub-secao: Titulos do cliente
Local oSecTit := TRSection():New(oSecCli, "Titulos", {"SE1"})
TRCell():New(oSecTit, "E1_NUM", "SE1", "Numero")
TRCell():New(oSecTit, "E1_PARCELA", "SE1", "Parc")
TRCell():New(oSecTit, "E1_VENCREAL","SE1", "Vencto")
TRCell():New(oSecTit, "E1_VALOR", "SE1", "Valor", "@E 999,999.99", 14)
// Total automatico
TRFunction():New(oSecTit:Cell("E1_VALOR"), NIL, "SUM", ;
oSecCli, "Subtotal cliente", "@E 999,999.99", NIL, .F.)
oReport:PrintDialog()
Return
Static Function ImprimeRel(oReport)
Local oSecCli := oReport:Section(1)
Local oSecTit := oReport:Section(1):Section(1)
SA1->(DBSetOrder(1))
SA1->(DBSeek(xFilial("SA1")))
oSecCli:Init()
While !SA1->(Eof()) .And. SA1->A1_FILIAL == xFilial("SA1")
oSecCli:PrintLine()
// Filhos: titulos em aberto desse cliente
SE1->(DBSetOrder(2)) // indice por cliente
SE1->(DBSeek(xFilial("SE1") + SA1->A1_COD + SA1->A1_LOJA))
oSecTit:Init()
While !SE1->(Eof()) .And. ;
SE1->E1_FILIAL == xFilial("SE1") .And. ;
SE1->E1_CLIENTE == SA1->A1_COD .And. ;
SE1->E1_LOJA == SA1->A1_LOJA
If Empty(SE1->E1_BAIXA) // so em aberto
oSecTit:PrintLine()
EndIf
SE1->(DBSkip())
EndDo
oSecTit:Finish()
SA1->(DBSkip())
EndDo
oSecCli:Finish()
Return
Variantes uteis
1. Quebra de pagina por cliente
oSecCli:SetPageBreak(.T.)
2. Total em pe de pagina (e nao por secao)
TRFunction():New(oSecTit:Cell("E1_VALOR"), NIL, "SUM", ;
oReport, "Total geral", "@E 999,999,999.99", NIL, .T.)
3. Cabecalho custom no relatorio
oReport:SetTitle("Titulos por Cliente - " + DToC(dDataBase))
oReport:SetMargins(2, 2, 2, 2)
oReport:SetLandscape() // paisagem
Exportacao automatica
TReport exporta nativamente em PDF, XLSX, CSV, HTML. O usuario escolhe na tela de impressao. Voce nao escreve nada extra — o framework cuida.
Pegadinhas
- Performance em quebras grandes — TReport carrega tudo em memoria. Em relatorio de 50k+ clientes, considere paginar ou usar TCQuery direto.
- Indices da SE1 — usar indice por cliente (geralmente E1_CLIENTE+E1_LOJA) e critico pra performance.
- SetTotalInLine(.F.) mostra total em linha separada (mais legivel).
.T.coloca total na linha do cliente. - oSecTit:Finish() obrigatorio antes de proximo cliente — sem isso, totalizadores nao resetam.
- Em base com muitas filiais, considere parametro de filial no Pergunte (MV_PAR01).
Verificacao
- Compile a User Function via
/advpl-compile - Acesse menu > Atualizacoes > Relatorios
- Cadastre rotina em
SX2/menu apontando praU_RelTitCli - Execute e teste PDF + XLSX