Artigo Clube Delphi Edição 12 - Filtrando Relatórios

Artigo da Revista Clube Delphi Edição 12.

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.

Artigos relacionados