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.