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.