Esse artigo faz parte da revista Clube Delphi edição 12. Clique aqui para ler todos os artigos desta edição



Atenção: por essa edição ser muito antiga não há arquivo PDF para download. Os artigos dessa edição estão disponíveis somente através do formato HTML.

Filtrando Relatórios

 

         Desde que comecei a visitar listas de discussão e a utilizar fórums na Internet (prática que aconselho a todos), reparo que algumas dúvidas sempre se repetem. Sobre o QuickReport por exemplo, sempre tem alguém perguntando como construir relatórios com filtro. Observe abaixo algumas recentes mensagens que capturei da caixa de email:

 

Caros Amigos,

 

Como posso  criar um relatório que imprima todos os pedidos de um determinado período, selecionado por cliente?”

 

 

Amigos da Lista,

 

Como criar um relatório que imprima os clientes que mais compraram, agrupado por produto e quantidade?

 

         A resposta para todas estas questões é a mesma: como o quickreport se conecta a um DataSet, o que devemos fazer é filtrar a Tabela ou a Query. Ou seja: não precisamos nos preocupar com a lógica da consulta no relatório, pois uma vez que a query foi construída para atender a necessidade do problema, o relatório irá apenas obedecer aos dados recebidos. Vejamos um exemplo na prática: Um relatório de pedidos no qual o usuário irá selecionar um período de datas para exibição dos dados. Para construir este relatório, iremos utilizar o banco de dados que acompanha o Delphi, o DBDEMOS.

         O relatório criado é um simples master/detail, envolvendo as tabelas ORDERS.DB e ITEMS.DB, o equivalente à pedidos e items. Crie um objeto DataModule e um objeto QuickReport, através do Object Repository, e configure-os conforme a listagem abaixo:

 

object DataModule2: TdataModule2

 

  TTable

    Active = True

    DatabaseName = 'DBDEMOS'

    TableName = 'ORDERS.DB'

    Left = 24

    Top = 8

    Name =TbOrders 

 

  TDataSource

    DataSet = tbOrders

    Left = 88

    Top = 8

    Name = dsOrders

 

  TTable

    Active = True

    DatabaseName = 'DBDEMOS'

    IndexFieldNames = 'OrderNo'

    MasterFields = 'OrderNo'

    MasterSource = dsOrders

    TableName = 'items.db'

    Left = 16

    Top = 78

    Name=tbItems

 

  TDataSource

    DataSet = tbItems

    Left = 88

    Top = 78

    Name=dsItems

 

TTABLE

    Active = True

    DatabaseName = 'DBDEMOS'

    IndexFieldNames = 'CustNo'

    MasterFields = 'CustNo'

    MasterSource = dsOrders

    TableName = 'customer.db'

    Left = 176

    Top = 78

    Name=TbCustomer

 

  TTable

    Active = True

    DatabaseName = 'DBDEMOS'

    IndexFieldNames = 'PartNo'

    MasterFields = 'PartNo'

    MasterSource = dsItems

    TableName = 'parts.db'

    Left = 170

    Top = 8

    Name = tbParts

 

 

 

Figura – Datamodule com os objetos Ttable

 

O objeto QuickReport será preenchido da seguinte forma:

 

QrBand1 -> 1 TqrLabel

QrBand2 -> 3 TqrDbText

QrSubDetail -> 3 qrDbtext

 

As propriedades dos objetos serão configuradas conforme a listagem abaixo:

 

  object QuickRep3: TquickRep

    DataSet = DataModule2.tbOrders

   

    TQRBand

     BandType = rbTitle

     Name=QrBand1

   

     TQRLabel (qrBand1)

        Left = 242

        Top = 8

        Caption = 'RELATÓRIO DE PEDIDOS'

        FontSize = 10

        Name=QRLabel1

  

   TQRBand

      Frame.DrawTop = True

      Color = clSilver     

      BandType = rbDetail

      Name=qrBand2

 

      TQRDBText (qrBand2)

        Left = 16

        Top = 8

        DataSet = DataModule2.tbOrders

        DataField = 'OrderNo'

        FontSize = 10

        Name=qrDbText1

    

      TQRDBText (qrBand2)

        Left = 280

        Top = 8

        DataSet = DataModule2.tbOrders

        DataField = 'SaleDate'

        FontSize = 10

        Name = QRDBText3

 

      TQRDBText (qrBand2)

        Left = 82

        Top = 8

        DataSet = DataModule2.tbCustomer

        DataField = 'Company'

        FontSize = 10

        Name=QRDBText2

 

    TQRSubDetail

       DataSet = DataModule2.tbItems

       Name=QrSubDetail1

    

   TQRDBText (QrSubDetail1)

        Left = 34

        Top = 8

        DataSet = DataModule2.tbItems

        DataField = 'PartNo'

        FontSize = 10

        Name= qrDbText4

 

 

 TQRDBText (qrsubDetail1)

        Left = 98

        Top = 8

        DataSet = DataModule2.tbParts

        DataField = 'Description'

        FontSize = 10

        Name= QRDBText5

 

 TQRDBText (qrSubDetail1)

        Left = 298

        Top = 8

        DataSet = DataModule2.tbItems

        DataField = 'Qty'

        FontSize = 10

        Name = qrdbText6

 

Para selecionar uma data de início e uma data de término um pequeno form foi criado, conforme mostra a figura abaixo:

 

 

Os objetos utilizados para construção deste formulário seguem abaixo:

 

Tform

Name=form1

 

TLabel

    Left = 72

    Top = 16

    Caption = 'Início:'

    Name=Label1

 

TLabel

    Left = 72

    Top = 72

    Caption = 'Término:'

    Name= Label2

 

TdateTimePicker

    Name=DateTimePicker1

    Left = 72

    Top = 32

 

TDateTimePicker

    Left = 72

    Top = 88

    Name= DateTimePicker2

 

TBitBtn

    Left = 104

    Top = 208

    Caption = 'Imprimir'

    Name=BitBtn1

 

E, para filtragem do relatório, o evento OnClick do botão Imprimir:

 

procedure TForm1.Button1Click(Sender: TObject);

begin

  Datamodule2.tbOrders.Filter:='SaleDate >= ''' +

  DateToStr(DateTimePicker1.Date) +

  ''' and SaleDate <= ''' + DateToStr(DateTimePicker2.Date)+ '''';

 

  Datamodule2.tbOrders.Filtered:=true;

  QuickrePort3.Preview;

end;

 

Nosso projeto já está pronto para ser executado:

 

Figura – Relatório em tempo de execução

 

Acabamos de ver, na prática, que toda a complexidade de um relatório fica nas mãos da query e da table.O QuickReport é a ferramenta para a construção visual do relatório. Para a construção da lógica, imagine que você está construindo uma consulta, e não um relatório. Com a consulta pronta, criar o relatório será bem mais fácil.

Mas ainda existem algumas peculiaridades. No exemplo que acabamos de construir, o filtro é realizado sobre a banda pai, que representa a tabela de pedidos (orders). Como a tabela orders é filtrada, automaticamente os filhos que não pertencem ao filtro são excluídos, pois temos um relacionamento entre os objetos Ttable – vide a propriedade MasterSource e MasterFields dos objetos. O problema aparece quando temos que realizar o filtro sobre a tabela filho. Se o nosso relatório evoluísse para a questão: “Quero visualizar todas as compras do produto X em determinado período, visualizando o número do pedido, o cliente, a data e a quantidade”. Preste atenção e perceba que estamos falando do mesmo relatório. A única diferença é que teremos que filtrar a tabela tbItems para exibir somente os pedidos que contenham determinado produto. Vejamos o que isso significa na prática.

Implemente nosso formulário de seleção, adicionando uma ComboBox e um objeto Ttable. Suas propriedades estão definidas abaixo:

 

object Table1: TTable

    DatabaseName = 'DBDEMOS'

    IndexName = 'ByDescription'

    TableName = 'parts.db'

    Left = 24

    Top = 120

 

object ComboBox1: TComboBox

    Left = 24

    Top = 160

    Width = 257

    Height = 21

 

O evento OnShow do form irá receber o código necessário para o preenchimento da ComboBox:

 

procedure TForm1.FormShow(Sender: TObject);

begin

  table1.Open;

  ComboBox1.Items.add('TODOS');

  While not Table1.Eof do begin

    ComboBox1.Items.Add(Table1.Fieldbyname('Description').asString);

    Table1.Next;

  End;

  ComboBox1.ItemIndex:=0;

end;

 

E o botão Imprimir será implementado de modo que também permita o filtro pela tabela filho:

 

procedure TForm1.BitBtn1Click(Sender: TObject);

begin

  Datamodule2.tbOrders.Filter:='SaleDate >= ''' +

  DateToStr(DateTimePicker1.Date) +

  ''' and SaleDate <= ''' + DateToStr(DateTimePicker2.Date)+ '''';

 

  if Table1.FindKey([ComboBox1.Text]) then begin

    Datamodule2.tbItems.Filter:='PartNo = '+Table1.Fieldbyname('PartNo').asString;

    Datamodule2.tbItems.Filtered:=True;

  End

  Else

    Datamodule2.TbItems.Filtered:=false;

 

  Datamodule2.tbOrders.Filtered:=true;

  QuickrePort3.Preview;

end;

 

 

         O resultado não será muito bom. Abaixo podemos visualizar o relatório com o produto ‘Alternate Inflation Regulator’ selecionado:

 

 

Na figura acima você pode perceber que o resultado visual do relatório ficou muito ruim. A resposta é simples. Como filtramos a tabela filho, e não a tabela pai, todos os registros da tabela pai serão exibidos, independente de conter ou não o referido produto como item. Para resolver este problema devemos tomar duas atitudes: a primeira é alterar a propriedade PrintIfEmpty, do objeto QrSubDetail1, para False. O resultado será melhorado:

 

 

O segundo passo é eliminar a impressão de bandas pai sem filhos. Podemos utilizar o evento OnBeforePrint, do objeto QrBand pai (qrBand2):

 

procedure TquickReport3.QRBand2BeforePrint(Sender: TQRCustomBand;

  var PrintBand: Boolean);

begin

  if Datamodule2.TbItems.RecordCount >0 then

    PrintBand:=True

  Else

    printBand:=False;

end;

 

Simples, não? O parâmetro PrintBand permite que indiquemos se a banda será impressa ou não. Dentro de um if para verificar se a banda filho possui registros, nosso problema está resolvido. Veja o resultado:

 

 

Existem casos em que o uso da Query será muito mais eficiente para a construção da lógica do relatório. Quando isto acontecer, basta utilizar a query em conjunto com o objeto QrGroup para criar o mesmo efeito de master/detail realizado neste exemplo.

Acredito que este artigo responderá muitas dúvidas em relação aos filtros no QuickReport, que, como percebemos, praticamente não existem. Este exemplo pode ser baixado no endereço www.clubedelphi.com.br/edicao12/filtrarqr.zip. Vários outros exemplos estão disponíveis na página do clube, no link de funções/exemplos, sob a categoria QuickReport.