Hoje irei mostrar como montar a lógica de um relatório sintético e analítico em AdvPL.
Muitos alunos me perguntam como montar em um mesmo relatório a lógica de ser sintético e analítico. Pois bem jovens, no artigo de hoje vou mostrar uma forma simples de se fazer.
Antes de começarmos, esse exemplo é feito usando TReport, mas a lógica você pode adaptar para HTML, FWMSPrinter, etc.
A lógica seria mais ou menos assim:
- Primeiro você tem uma tela de parâmetros, como por exemplo, data de/até
- Nessa tela de parâmetros, você tem que ter um parâmetro para definir se é Sintético ou Analítico
- Ao confirmar o relatório, você faz a primeira query, utilizando group by e sum que será a query do sintético
- No laço de repetição dos dados sintéticos, você vai testar o parâmetro, se for analítico, você vai fazer uma query buscando todos os itens do registro atual sintético, e irá imprimir eles
Com a lógica acima, teríamos um cenário onde por exemplo, na Nota 1 de valor R$ 100, se for analítico irá imprimir todos os itens, senão, já vai para a próxima nota.
Abaixo o print do resultado sintético:
E do resultado analítico:
Abaixo o código fonte do exemplo citado:
//Bibliotecas
#Include "Protheus.ch"
#Include "TopConn.ch"
/*/{Protheus.doc} ACOMR01
Relatório - Listagem de Documentos de Entrada
@author Atilio
@since 05/07/2021
@version 1.0
@example
u_ACOMR01()
/*/
User Function ACOMR01()
Local aArea := GetArea()
Local oReport
Local aPergs := {}
Local dDataDe := FirstDate(Date())
Local dDataAt := LastDate(Date())
Local nTipo := 1
Local cNotaDe := Space(TamSX3('D1_DOC')[1])
Local cNotaAt := StrTran(cNotaDe, ' ', 'Z')
Local cProdDe := Space(TamSX3('B1_COD')[1])
Local cProdAt := StrTran(cProdDe, ' ', 'Z')
Local cFornDe := Space(TamSX3('A2_COD')[1])
Local cFornAt := StrTran(cFornDe, ' ', 'Z')
Private aTamD1 := TamSX3('D1_TOTAL')
Private cMasD1 := PesqPict('SD1', 'D1_TOTAL')
//Adiciona os parâmetros
aAdd(aPergs, {1, "Data De", dDataDe, "", ".T.", "", ".T.", 80, .F.})
aAdd(aPergs, {1, "Data Até", dDataAt, "", ".T.", "", ".T.", 80, .T.})
aAdd(aPergs, {2, "Tipo", nTipo, {"1=Sintético", "2=Analítico"}, 090, ".T.", .F.})
aAdd(aPergs, {1, "Documento De", cNotaDe, "", ".T.", "", ".T.", 100, .F.})
aAdd(aPergs, {1, "Documento Até", cNotaAt, "", ".T.", "", ".T.", 100, .T.})
aAdd(aPergs, {1, "Produto De", cProdDe, "", ".T.", "SB1", ".T.", 80, .F.})
aAdd(aPergs, {1, "Produto Até", cProdAt, "", ".T.", "SB1", ".T.", 80, .T.})
aAdd(aPergs, {1, "Fornecedor De", cFornDe, "", ".T.", "SA2", ".T.", 60, .F.})
aAdd(aPergs, {1, "Fornecedor Até", cFornAt, "", ".T.", "SA2", ".T.", 60, .T.})
//Se a pergunta for confirmada
If ParamBox(aPergs, "Informe os parâmetros", , , , , , , , , .F., .F.)
MV_PAR03 := Val(cValToChar(MV_PAR03))
oReport := fReportDef()
oReport:PrintDialog()
EndIf
RestArea(aArea)
Return
/*-------------------------------------------------------------------------------*
| Func: fReportDef |
| Desc: Função que monta a definição do relatório |
*-------------------------------------------------------------------------------*/
Static Function fReportDef()
Local oReport
Local oSectSin := Nil
Local oSectAna := Nil
//Criação do componente de impressão
oReport := TReport():New( "ACOMR01",; //Nome do Relatório
"Relatório de Documentos de Entrada",; //Título
,; //Pergunte ... Se eu defino a pergunta aqui, será impresso uma página com os parâmetros, conforme privilégio 101
{|oReport| fRepPrint(oReport)},; //Bloco de código que será executado na confirmação da impressão
) //Descrição
oReport:SetTotalInLine(.F.)
oReport:lParamPage := .F.
oReport:oPage:SetPaperSize(9)
oReport:SetPortrait()
oReport:SetLineHeight(50)
oReport:nFontBody := 10
//Criando a seção
oSectSin := TRSection():New( oReport,;
"Dados",;
{"QRY_SIN"})
oSectSin:SetTotalInLine(.F.)
//Colunas do relatório
TRCell():New(oSectSin, "D1_DOC", "QRY_SIN", "Documento", /*Picture*/, 12, /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSectSin, "D1_SERIE", "QRY_SIN", "Série", /*Picture*/, 03, /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSectSin, "D1_DTDIGIT", "QRY_SIN", "Data", /*Picture*/, 12, /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSectSin, "D1_FORNECE", "QRY_SIN", "Forn.", /*Picture*/, 08, /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSectSin, "A2_NOME", "QRY_SIN", "Nome", /*Picture*/, 15, /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSectSin, "TOTAL", "QRY_SIN", "Vlr. Total", cMasD1, aTamD1[1], /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSectSin, "VLR_IPI", "QRY_SIN", "Vlr. IPI", cMasD1, aTamD1[1], /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSectSin, "VLR_PROP", "QRY_SIN", "Vlr. Prop.", cMasD1, aTamD1[1], /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSectSin, "VLR_ST", "QRY_SIN", "Vlr. ST", cMasD1, aTamD1[1], /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
//Criando a seção
oSectAna := TRSection():New( oSectSin,;
"Analítico",;
{"QRY_ANA"})
oSectAna:SetTotalInLine(.F.)
//Colunas do relatório
TRCell():New(oSectAna, "D1_ITEM", "QRY_ANA", "Item", /*Picture*/, 06, /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSectAna, "D1_COD", "QRY_ANA", "Produto", /*Picture*/, 10, /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSectAna, "B1_DESC", "QRY_ANA", "Descrição", /*Picture*/, 30, /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSectAna, "D1_TOTAL", "QRY_ANA", "Vlr. Total", cMasD1, aTamD1[1], /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSectAna, "D1_VALIPI", "QRY_ANA", "Vlr. IPI", cMasD1, aTamD1[1], /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSectAna, "D1_VALICM", "QRY_ANA", "Vlr. Prop.", cMasD1, aTamD1[1], /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
TRCell():New(oSectAna, "D1_ICMSRET", "QRY_ANA", "Vlr. ST", cMasD1, aTamD1[1], /*lPixel*/,/*{|| code-block de impressao }*/,/*cAlign*/,/*lLineBreak*/,/*cHeaderAlign */,/*lCellBreak*/,/*nColSpace*/,/*lAutoSize*/,/*nClrBack*/,/*nClrFore*/,/*lBold*/)
Return oReport
/*-------------------------------------------------------------------------------*
| Func: fRepPrint |
| Desc: Função que imprime o relatório |
*-------------------------------------------------------------------------------*/
Static Function fRepPrint(oReport)
Local aArea := GetArea()
Local cQrySin := ""
Local cQryAna := ""
Local oSectSin := Nil
Local oSectAna := Nil
Local nAtual := 0
Local nTotal := 0
//Pegando as seções
oSectSin := oReport:Section(1)
oSectAna := oReport:Section(1):Section(1)
//Faz uma consulta buscando os dados sintéticos
cQrySin := " SELECT " + CRLF
cQrySin += " D1_DOC, " + CRLF
cQrySin += " D1_SERIE, " + CRLF
cQrySin += " D1_DTDIGIT, " + CRLF
cQrySin += " D1_FORNECE, " + CRLF
cQrySin += " A2_NOME, " + CRLF
cQrySin += " SUM(D1_TOTAL) AS TOTAL, " + CRLF
cQrySin += " SUM(D1_VALIPI) AS VLR_IPI, " + CRLF
cQrySin += " SUM(D1_VALICM) AS VLR_PROP, " + CRLF
cQrySin += " SUM(D1_ICMSRET) AS VLR_ST " + CRLF
cQrySin += " FROM " + CRLF
cQrySin += " " + RetSQLName('SD1') + " SD1 " + CRLF
cQrySin += " INNER JOIN " + RetSQLName('SA2') + " SA2 ON ( " + CRLF
cQrySin += " A2_FILIAL = '" + FWxFilial('SA2') + "' " + CRLF
cQrySin += " AND A2_COD = D1_FORNECE " + CRLF
cQrySin += " AND A2_LOJA = D1_LOJA " + CRLF
cQrySin += " AND SA2.D_E_L_E_T_ = ' ' " + CRLF
cQrySin += " ) " + CRLF
cQrySin += " WHERE " + CRLF
cQrySin += " D1_FILIAL = '" + FWxFilial('SD1') + "' " + CRLF
cQrySin += " AND D1_DTDIGIT >= '" + dToS(MV_PAR01) + "' " + CRLF
cQrySin += " AND D1_DTDIGIT <= '" + dToS(MV_PAR02) + "' " + CRLF
cQrySin += " AND D1_DOC >= '" + MV_PAR04 + "' " + CRLF
cQrySin += " AND D1_DOC <= '" + MV_PAR05 + "' " + CRLF
cQrySin += " AND D1_COD >= '" + MV_PAR06 + "' " + CRLF
cQrySin += " AND D1_COD <= '" + MV_PAR07 + "' " + CRLF
cQrySin += " AND D1_FORNECE >= '" + MV_PAR08 + "' " + CRLF
cQrySin += " AND D1_FORNECE <= '" + MV_PAR09 + "' " + CRLF
cQrySin += " AND D1_TIPO NOT IN ('B', 'D') " + CRLF
cQrySin += " AND SD1.D_E_L_E_T_ = ' ' " + CRLF
cQrySin += " GROUP BY " + CRLF
cQrySin += " D1_DOC, " + CRLF
cQrySin += " D1_SERIE, " + CRLF
cQrySin += " D1_DTDIGIT, " + CRLF
cQrySin += " D1_FORNECE, " + CRLF
cQrySin += " A2_NOME " + CRLF
cQrySin += " ORDER BY " + CRLF
cQrySin += " D1_DTDIGIT, " + CRLF
cQrySin += " D1_DOC " + CRLF
TCQuery cQrySin New Alias "QRY_SIN"
TCSetField("QRY_SIN", "D1_DTDIGIT", "D")
//Contando o total de registros
Count To nTotal
//Se houver dados
If nTotal != 0
//Setando a régua e fazendo o laço para imprimir os dados
oReport:SetMeter(nTotal)
QRY_SIN->(DbGoTop())
nAtual := 0
oSectSin:Init()
While ! QRY_SIN->(EoF())
nAtual++
oReport:SetMsgPrint("Imprimindo (nota " + cValToChar(nAtual) + " de " + cValToChar(nTotal) + ")...")
oReport:IncMeter()
//Imprimindo a linha atual
oSectSin:PrintLine()
//Se for do tipo analítico
If MV_PAR03 == 2
//Criando a consulta analítica
cQryAna := " SELECT " + CRLF
cQryAna += " D1_ITEM, " + CRLF
cQryAna += " D1_COD, " + CRLF
cQryAna += " B1_DESC, " + CRLF
cQryAna += " D1_TOTAL, " + CRLF
cQryAna += " D1_VALIPI, " + CRLF
cQryAna += " D1_VALICM, " + CRLF
cQryAna += " D1_ICMSRET " + CRLF
cQryAna += " FROM " + CRLF
cQryAna += " " + RetSQLName('SD1') + " SD1 " + CRLF
cQryAna += " INNER JOIN " + RetSQLName('SB1') + " SB1 ON ( " + CRLF
cQryAna += " B1_FILIAL = '" + FWxFilial('SB1') + "' " + CRLF
cQryAna += " AND B1_COD = D1_COD " + CRLF
cQryAna += " AND SB1.D_E_L_E_T_ = ' ' " + CRLF
cQryAna += " ) " + CRLF
cQryAna += " WHERE " + CRLF
cQryAna += " D1_FILIAL = '" + FWxFilial('SD1') + "' " + CRLF
cQryAna += " AND D1_DOC = '" + QRY_SIN->D1_DOC + "' " + CRLF
cQryAna += " AND D1_SERIE = '" + QRY_SIN->D1_SERIE + "' " + CRLF
cQryAna += " AND D1_FORNECE = '" + QRY_SIN->D1_FORNECE + "' " + CRLF
cQryAna += " AND D1_TIPO NOT IN ('B', 'D') " + CRLF
cQryAna += " AND SD1.D_E_L_E_T_ = ' ' " + CRLF
cQryAna += " ORDER BY " + CRLF
cQryAna += " D1_ITEM " + CRLF
TCQuery cQryAna New Alias "QRY_ANA"
//Enquanto houver dados analíticos
oSectAna:Init()
While ! QRY_ANA->(EoF())
oSectAna:PrintLine()
QRY_ANA->(DbSkip())
EndDo
QRY_ANA->(DbCloseArea())
oSectAna:Finish()
//Encerra o Sintético, e inicializa novamente
oSectSin:Finish()
oSectSin:Init()
EndIf
QRY_SIN->(DbSkip())
EndDo
oSectSin:Finish()
EndIf
QRY_SIN->(DbCloseArea())
RestArea(aArea)
Return
Bom pessoal, por hoje é só.
Abraços e até a próxima.


Bom dia.
Conteúdo excepcional. Parabéns.
Grande Caio.
Obrigado pelo comentário e feedback jovem.
Abração.