Esse artigo faz parte da revista Clube Delphi edição 3. 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. 

 

Sistema de Biblioteca – Parte III

 

Estamos novamente as voltas com nosso sistema de biblioteca. Vamos relembrar todo o processo e como o sistema funciona:

O sistema tem como objetivo de controlar os empréstimos e devoluções de livros em um biblioteca. A aplicação não foi criada visando nenhuma situação real, e tem o puro objetivo de facilitar o aprendizado ao acesso de banco de dados com o Delphi. Nesta biblioteca virutal, temos apenas três  cadastros: livros, Leitores e Empréstimos (assim como devoluções). Na primeira edição do jornal vimos o cadastro de leitores e na segunda o cadastro de livros. Se você não acompanhou as edições anteriores do nosso Jornal, visite nosso site (www,clubedelphi.com.br) ou mande um email para admin@clubedelphi.com.br, para saber como adquirí-las

Relembrando, criamos a estrutura das tabelas da seguinte forma:

 

Figura 1: formulário modelo

 

Como vimos na etapa passada, já possuímos um Template (modelo) para todos os formulários de cadastro do nosso sistema. Este modelo foi criado exatamente para economizarmos tempo na hora da criação do form, já que muitas das características são comuns aos nossos cadastros.

Continuaremos agora criando o formulário de cadastro de Empréstimos de livros. Este form ficará parecido com a figura 3

O primeiro passo é criar as tabelas no Database Desktop, caso já não estejam criadas.

Vamos agora iniciar a programação – entre no Delphi e abra o nosso projeto, chamado Biblio. Crie um novo formulário, e chame-o de cadatro de empréstimos. Para isto, usaremos o nosso modelo de form de cadastro, criado na edição anterior. O modelo já possui muito do trabalho pronto, e isto é um grande ponto positivo. Clique no menu File, opção New, para chamar o Object Repository.

 

Figura 2: repositório de objeto

 

Clique duas vezes sobre o ícone de nosso modelo. Não esqueça de selecionar a opção

Copy, na parte inferior do Repositório, um novo formulário será criado.

Salve o projeto e chame o arquivo de CadEmprestimo e o nome do formulário de FrmCadEsprestimos. Configure o objeto Ttable e DataSource presente no formulário com as características abaixo.

 

Name : TbCadEmprestimo

TableName : Empréstimo.db

Caption : Cadastro de Empréstimos

 

Name : DsEmprestimo

DataSet : TbCadEmprestimo

 

Figura 3 – Fomulário de cadastro de empréstimo

 

Iremos necessitar de mais três objetos TTable, um para referenciar a tabela de livros emprestados (tabela filha), outra para apontar para a tabela de livros, e outra para referenciar a tabela de clientes. Configure-as de acordo com as propriedades de cada objeto  a seguir:

 

Objeto TDataSource

Name dsLivrosEmp

Dataset tbLivrosEmp

 

Objeto Ttable Objeto Table3

Name tbLivro

DatabaseName dbBiblio

TableName Livros.db

 

Objeto TDataSource

Name dsLivro

Dataset tbLivro

 

Objeto Ttable

Name tbLeitor

DatabaseName dbBiblio

TableName Leitor.db

 

Objeto TDataSource

Name dsLeitor

Dataset tbLeitor

 

Objeto Ttable

Name tbLivrosEmp

DatabaseName Biblio

TableName LivroEmp.db

MasterSource dsEmprestimo

MasterFields Codigo

Tabela 9: Objeto DBText1

Vamos criar os objetos de edição, de acordo com a figura 3:

Objeto TDBtext

DataSource dsEmprestimo

DataField Codigo

 

Objeto TDBedit1

DataSource dsEmprestimo

DataField DataEmp

 

Para exibir o nome do leitor, usaremos o objeto TDBLookUpComboBox. Este objeto é muito parecido com o TDBComboBox, mas com uma diferença fundamental: o TDBComboBox exibe valores estáticos, ou seja, os que foram pré definidos no sistema. Já o objeto TDBLookUpComboBox exibe valores dinâmicos, oriundos de outra tabela. Por exemplo, um campo como ‘Estado Civil’ poderia ser bem implementado com um TDBComboBox, pois os tipos de ‘Estado Civil’ existentes são em média estáticos, não mudam com freqüência. Já um campo do tipo profissão, seria melhor utilizado com um objeto do tipo TDBLookUpComboBox, pois teríamos uma tabela de profissões, e os valores seriam lidos desta tabela. No nosso caso, temos uma tabela de leitores, e o LookUpComboBox exibirá os valores desta tabela. Vejamos como configurar corretamente este objeto:

 

DBLookupComboBox

DataSource – Representa a tabela onde os dados serão gravados – neste  caso é a tabela de Empréstimos.

 

DataField – Representa em qual campo será salvo o valor escolhido pelo usuário, na tabela de destino. No nosso caso, é o campo Leitor, da tabela de empréstimos.

 

ListSource – Representa a tabela de origem, ou seja, a tabela que fornecerá os dados para seleção. Selecione o datasource dsLeitor, indicando a tabela de leitores como a tabela de origem.

 

ListField – Indica qual campo será mostrado para o usuário – atenção, pois o campo exibido para o usuário não significa necessariamente o campo que será salvo na tabela. No nosso caso,  será exibido o nome do leitor para o usuário, mas somente o código será salvo na tabela de empréstimos. Portanto, selecione o campo Nome.

 

KeyField – Indica o valor de origem que será usado para preencher o campo da tabela de destino. Em ListField indicamos qual campo da tabela de origem será mostrado para o usuário, e em KeyField indicamos qual campo da tabela de origem será usado para gravar na tabela de destino. Neste caso, devemos selecionar o campo código – pois gravaremos o código do leitor em empréstimos. Veja como ficaram as propriedades do objeto TDBLookupComboBox:

 

DataSource dsEmprestimo

DataField Leitor

ListSource dsLeitor

ListField Nome

KeyField Codigo

 

Vamos também vincular o objeto TDBGrid, com a tabela de LivrosEmp.

Esta grade irá exibir os livros que estão sendo emprestados ao leitor. Veja na tabela

abaixo, as propriedades do objeto

TDBGrid:

 

DataSource  dsLivrosEmp

ReadOnly    True

 

Repare também que há dois botões abaixo da DBGrid. Estes botões irão, respectivamente, incluir e cancelar inclusão de  livros .

Insira dois objetos da classe TButton, posicione-os de acordo com a figura 6, e configure suas propriedades de acordo com as tabelas a seguir:

 

TButton

Caption  &Incluir Livro

Name btIncuirLivro

 

TButton Tabela 14: Objeto Button2

Caption  &Cancelar Inclusão

Name btExcluirLivro

 

Ok! A interface de nossa tela está montada! Iremos agora ao código da aplicação:

Todo formulário de cadastro do nosso sistema possui umaa procedure chamada ControlaBotoes. Para quem não acompanhou o curso, esta procedure é responsável por habilitar ou deabilitar os objetos (botões e panels) na tela, conforme a necessidade. Por exemplo, ao clicar no botão ‘Incluir’, a procedure desabilitará os botões de ‘alteração e Exclusão. Ao clicar no botão ‘Confirmar’’, essa irá desabilitar os objetos de edição e habilitar novamente os botões de ‘Inclusão’, ‘Alteração’ e ‘Exclusão’, amarrando a operação do sistema a este procedimento.

O formulário de modelo possui toda a base da procedure controla botões. Precisamos apenas fazer pequenas alterações para adaptar a rotina a nossa tela:

 

procedure TFrmCadEmprestimos. ControlaBotoes (b:Boolean);

begin

btIncluir.Enabled:=b;

btAlterar.Enabled:=b;

btExcluir.Enabled:=b;

btConfirmar.Enabled:=not b;

btCancelar.Enabled:=not b;

dsEmprestimo.Enabled:=not b;

dsLivrosEmp.Enabled:=not b;

end;

 

Antes de codificarmos os eventos OnClick dos botões, devemos chamar atenção para um detalhe importante. Este formulário terá uma lógica diferente

dos cadastros anteriores. Aqui, utilizaremos um recurso ainda não explorado

em nosso curso, chamado Transação.

Para quem nunca ouviu falar ou nunca trabalhou com transações, vamos detalhar um pouco o assunto:

Os gerenciadores de banco de dados ‘Cliente/Servidor’, sempre trabalham dessa forma. A transação é um seção aberta por um determinado usuário. A abertura de uma seção dá poder ao desenvolvedor de decidir quando efetivamente as informações digitadas pelo usuário estarão salvas no banco de dados. Em uma tela de pedidos, por exemplo, o sistema pode abrir uma seção (Transação) quando o usuário solicitar uma inclusão. Somente quando o usuário terminar de digitar todas as informações do pedido, inclusive os itens, o sistema confirma a transação (Commit), salvando as alterações no banco de dados. Caso o usuário desista da operação, a transação pode ser cancelada (Rollback), desfazendo todas as alterações feitas desde o início da transação corrente. Podemos imaginar também um outro tipo de aplicação de transações, Uma alteração em Batch. Imaginemos uma situação onde o sistema precisa realizar várias alterações em um banco de dados de uma só vez. Por exemplo, atualizar preços de produtos de acordo com um porcentual de reajuste. A aplicação teria que realizar um loop em todos os produtos que seriam reajustados e calcular o novo valor. Se o sistema travasse por algum motivo no meio da operação, teríamos produtos atualizados e não atualizados, e para separá-los seria um problema bem grande. Com transações, o problema seria resolvido rapidamente: O sistema inicia uma transação, efetiva todas elas, e depois confirma a transação com (Commit). Caso o sistema tivesse caído durante a operação, os novos preços não seriam salvos e o reajuste poderia ser reiniciado sem problemas.

Os recursos de transação são originários de SGBD’s. O paradox, a partir

da versão 7.0, passou a disponibilizar o recurso de transação para tabelas locais, através do BDE. É claro que uma transação realizada por um Sistema Gerenciador de Banco de Dados (SGDB) terá um controle e uma segurança muito maior.

A transação com o paradox ilustrada aqui pode ser aplicada também a SGDB’s. Veremos as limitações do uso de transações com o paradox mais adiante.

Os métodos para controle de transações no Delphi estão disponíveis através do objeto TDataBase. Precisamos, então, de um objeto desta classe em nosso formulário de empréstimos. Para isto, clique na palheta BDE e dê um duplo clique no objeto TdataBase que automaticamente será incluído no Form.

 

Com o objeto TdataBase no formulário, clique duas vezes sobre ele, para abrir a caixa de diálogo mostrada na figura 4.

 

Figura 4 – propriedades do TdataBase

 

Nesta janela, selecione o ComboBox referente a propriedade Alias name. escolha nosso alias Biblio. Na propriedade Name, digite DataBaseBiblio e clique em Ok.

Mais uma propriedade deve ser configurada – TransIsolation, que deve ser setada para tiDirtyRead. A propriedade TransIsolation indica como as transações concorrentes irão se comportar. Veja abaixo a descrição de cada um dos possíveis valores para esta propriedade:

 

tiReadCommitted – A transação atual pode visualizar transações já confirmadas por outras seções.

 

tiRepeatableRead – Aqui a transação permanece isolada de outras transações

concorrentes. Transações confirmadas durante a seção atual não serão percebidas,

até que a transação atual seja encerrada.

 

tiDirtyRead – Nesta configuração, a transação atual pode visualizar as transações

concorrentes que foram confirmadas, e inclusive, as que ainda não foram salvas!

Como estamos utilizando o paradox, e não há nenhum sistema gerenciando o controle de acesso aos dados, as transações no paradox só podem ser utilizadas na forma tiDirtyRead.

Não existe mistério na propriedade TransIsolation, é fácil de ser entendida se analisarmos como funciona o processo. Num banco de dados Cliente/Servidor, as transações realmente permanecem em cache, pois o SGDB está gerenciando isto. No paradox, não há sistema por trás gerenciando os chaches de transação. Portanto, todas as alterações feitas após o início de uma transação, estarão realmente sendo gravadas na tabela principal,disponibilizando os dados imediatamente para as outras seções concorrentes. Quando é dado um Rollback, todas as alterações são desfeitas, baseado no log local. Agora, se o sistema travar ou a máquina for desligada, as operações não serão revertidas, pois já estavam salvas na tabela. O Commit apenas confirma as últimas operações e encerra a transação atual.

Agora devemos indicar que os objetos TTable do nosso formulário estarão vinculados ao objeto TDataBase, pois quando iniciarmos uma transação com o TDataBase, as inclusões e alterações feitas através dos objetos Table do Form deverão pertencer a transação corrente. Para isto, basta alterar a propriedade DatabaseName para DataBaseBiblio, que foi o nome referenciado ao objeto Database1.

Com as tabelas apontando para o objeto DataBase, e esse apontando para o nosso alias Biblio, podemos agora iniciar a codificação do formulário.

Iniciaremos pelo evento OnShow do Formulário:

 

procedure TFrmCadEmprestimos.FormShow (Sender: TObject);

begin

  Database1.Connected:=true;

  tbCadEmprestimo.Open;

  tbLivrosEmp.Open;

  tbLivro.Open;

  tbLeitor.Open;

  ControlaBotoes(True);

end;

 

Repare na variável Opc. Ela indicará o botão ‘Confirmar’ qual tipo de operação será confirmada. A variável é do tipo String, e deve ser decalarada no topo do formulário, como segue:

 

var

  Form1: TForm1;

  OPC: string;

 

Após este procedimento, vemos o método StartTransaction:

 

Database1.StarTransaction;

 

Este método irá iniciar uma transação.

Nota: só é possível a existência de uma transação por aplicação

 

Após iniciar a transação, é dado um ‘Append’ e um ‘Post’ na tabela principal, emprestimos. Isto é feito para que o campo chave seha imediatamente gerado no clique do botão ‘Incluir’, liberando a sequencia de numeração para outras máquinas que estejam incluindo empréstimos. Lgo após, é chamado o método Edit, para que a tabela permaneça em modo de edição. Por último, a procedure ControlaBotoes, que irá habilitar os botões de ‘Confirmação’ e ‘Cancelamento’.

Vejamos p bptão ‘Confimar’:

 

procedure TFrmCadEmprestimos.btConfirmarClick(Sender:TObject);

begin

if (opc=‘I’) or (opc= ‘A’) then

  begin

    TbCadEmprestimo.Post;

    Database1.Commit;

  end;

ControlaBotoes(True);

end;

 

Simples, não? Verifica-se o valor da varável Ope, e caso essa seja igual a ‘I’ ou ‘A’, chama-se o método Post, confimando a inclusão do registro atual, e logo após, o método Commit, confimando as alterações feitas no banco de dados liberando a transação atual. Após, é chamado a procedure ‘ControlaBotoes’, para novamente habilitar o botão ‘Incluir’ e os demais.

Com o botão ‘Cancelar’, também não há maiores dificuldades:

 

procedure TForm1.btCancelarClick(Sender:TObject);

begin

  DataBase1.RollBack;

  ControlaBotoes(True);

end;

 

Aqui vemos o terceiro método relacionado a transações, o método Rollback. Este reverte todas as operações realizadas desde a execução do método StartTransaction. E por último, a procedure ControlaBotoes, normalizando o estado dos botões do Form.

Vamos ao botão ‘Alterar’:

 

procedure TFrmCadEmprestimos.btAlterarClick(Sender:TObject);

begin

  Codigo:=-1;

  FrmConsEmprestimos.ShowModal;

  if codigo>0 then

  begin

    TbCadEmprestimo.FindKey([Codigo]);

    opc:=‘A’;

    DataBase1.StartTransaction;

    TbCadEmprestimo.Edit;

  end;

end;

 

Primeiramente temos o uso da variável código, do tipo integer, a variável deve ser declarada no topo do formulário, como segue:

 

var

Form1: TForm1;

OPC: string;

Codigo: integer;

NroSerie:Integer;

 

Como mostrado, crie também uma variável chamada NroSerie, pois a utilizaremos

mais adiante.

O sistema de alteração é parecido com nossos formulários anteriores: A variável código é inicializada com -1, e logo após o formulário de consulta é chamado. No formulário de consulta, o usuário irá selecionar um empréstimo, setando o código do empréstimo para a variável código. A construção do formulário de consulta de empréstimos será mostrada mais adiante.

Após o método ShowModal, o conteúdo da variável código é comparado com zero. Com isso, podemos saber se o usuário realmente selecionou algum empréstimo, ou se ele clicou em cancelar no Form de consulta. Caso um empréstimo tenha sido selecionado, devemos posicionar a tabela de empréstimos no registro selecionado pelo usuário. Isto é feito com o método FindKey:

 

TbEmprestimo.FindKey([Codigo]);

 

O método FindKey, como já sabemos, faz uma pesquisa através do índice atual, que no caso, é a chave primária por código.

Após posicionar a tabela no registro selecionado, a variável OPC recebe o valor ‘A’, seguido pelo método StartTransaction. Em seguida, a tabela é posta em modo de edição, através do método Edit:

 

opc:=‘A’;

DataBase1.StartTransaction;

TbCadEmprestimo.Edit;

 

O botão ‘Excluir’ é muito semelhante ao ‘Alterar’. Veja:

 

procedure TFrmCadEmprestimos.btExcluirClick(Sender:TObject);

begin

  Codigo:=-1;

  FrmConsEmprestimos.ShowModal;

  if codigo>0 then

    begin

      tbCadEmprestimo.FindKey([Codigo]);

      opc:=‘E’;

      PanelDados.Enabled:=False;

    end;

end;

 

No caso de exclusão, a variável opc recebe o valor ‘E’. Repare também que não há necessidade de se chamar o método StartTransaction, pois o usuário não poderá alterar os dados. O objeto PanelDados (que contém todos os objeto de edição) é desabilitado justamente para isso. No momento da exclusão, os dados só poderão ser visualizados, e não editados. Repare que o Panel é novamente habilitado na procedure ControlaBotoes.

Adaptações no botão ‘Confirmar’:

 

procedure TfrmCadEmprestimos.btConfirmarClick(Sender:TObject);

begin

if (opc=‘I’) or (opc=‘A’) then

begin

  tbCadEmprestimo.Post;

  Database1.Commit;

end;

if (opc=‘E’) then

begin

  tblivrosEmp.First;

  while not tbLivrosEmp.Eof do

  begin

    tblivrosEmp.Delete;

    tbCadEmprestimo.Delete;

  end;

end;

ControlaBotoes(True);

end;

 

Para o botão ‘Confirmar’ funcionar em caso de exclusão, basta incluir a rotina em destaque. Primeiramente excluímos todos os livros daquele empréstimo (lembre-se que a tabela de livros emprestados está vinculada (mastersource - masterfields) com a de empréstimos), e depois o empréstimo selecionado é excluído. Repare que não há necessidade de executar o método Commit, pois na exclusão nenhuma transação é iniciada.

Vejamos como fica o código do Botão ‘Cancelar’:

 

procedure TFrmCadEmprestimos.btCancelarClick(Sender:TObject);

begin

if (opc=‘I’) or (opc=‘A’)

then

DataBase1.RollBack;

ControlaBotoes(True);

end;

 

A única adaptação necessária ao botão cancelar é a verificação da variável opc antes do método rollback. Neste caso, o método rollback somente será executado caso o form esteja em modo de inclusão ou alteração.

Ok! Toda a funcionalidade dos botões principais da aplicação está terminada!

Construindo o Form de Consulta de Empréstimos

 

Para concluir os botões ‘Alterar’ e ‘Excluir’, devemos criar um formulário de consulta empréstimos, para que o usuário possa selecionar qual empréstimo será alterado ou excluído. O nosso formulário de consulta ficará parecido com a figura 5

Crie um novo formulário através do menu File à Newà Form.  Salve a unidade de consEmprestimo. Modifique as propriedades de acordo com a tabela abaixo:

 

Name FrmConsEmpresimos

BorderStyle bsDialog

Position poScreenCenter

Caption Consulta de Empréstimos

 

Figura 5 – o form de consulta de empréstimos

 

Coloque os objetos de acordo com a figura 8, e configure suas propriedades, como segue:

 

Name qyCadEmprestimo

DatabaseName bsDialog

 

Name DataSource1

Dataset qyCadEmprestimo

 

Name DBGrid1

DataSource DataSource1

 

Name Bitbtn1

Kind kdOK

ModalResult mbOK

 

Name Bitbtn2

Kind kdCancel

ModalResult mbCancel

 

Name dtInicio

 

Name dtFim

 

Repare que a grade deverá exibir somente os empréstimos em aberto. Podemos verificar isto através do campo DataDevolucao, que indica a data em que o empréstimo foi encerrado. Todos os registros cujo campo DataDevolucao estiverem em branco, representam empréstimos não encerrados. Vamos configurar a propriedade SQL do objeto TQuery, criando uma consulta SQL para filtrar a tabela de empréstimos através do campo DataDevolucao:

 

SELECT Emprestimo.Codigo,

Emprestimo.DataEmp,

Leitor.Nome

FROM Emprestimo, Leitor

WHERE Emprestimo.Leitor=

Leitor.Codigo

AND DataDevolucao is

null

AND DataEmp >= :inicio

and DataEmp <= :fim

ORDER BY Emprestimo.DataEmp

 

Analisemos a query acima:

O comando SELECT, exibirá apenas para o usuário os campos Código do Empréstimo, data em que o empréstimo foi realizado, e o Nome do leitor que pegou os livros emprestados. Na cláusula FROM, as tabelas de empréstimos e leitores são selecionadas (já que o nome do leitor é proveniente da tabela de leitores). E na cláusula WHERE temos duas condições; a primeira:

Emprestimo.Leitor=Leitor.Código

Serve para unir as duas tabelas em questão. Já a Segunda:

DataDevolucao is null

Filtrará apenas os registros que estejam com o campo ‘DataDevolucao’ em branco. E a terceira:

DataEmp >= :inicio and DataEmp <= :fim


Irá filtrar somente os registros na qual a data de empréstimo seja maior que inicio e menor que fim. Inicio e fim são duas variáveis criadas dentro do código SQL.

O símbolo de dois pontos “:” identifica que inicio e fim são variáveis, e não nomes

de campo. Estas variáveis estarão acessíveis no Delphi através da propriedade

ParamByName do objeto ‘Query’.

A última linha indica que o resultado será ordenado pela data do empréstimo:

ORDER BY Emprestimo.DataEmp.


A Query será atualizada em três momentos: na abertura do formulário e nos eventos OnCloseUp dos objetos ‘TDateTimePicker’. Este evento é usado no lugar do evento OnChange, pois o evento OnCloseUp ocorre depois que foi selecionado a data no objeto ‘TDateTimePicker’. Já o evento OnChange ocorre, a data selecionada ainda não foi passada para a propriedade Date do objeto.

Começaremos pelo primeiro objeto TDateTimePicker, cujo nome é ‘dtInicio’:

 

Procedure TfrmConsEmprestimos.dtInicioCloseUp(Sender:TObject);

begin

  Screen.Cursor:=crHourGlass;

  QyCadEmprestimo.Close;

  QyCadEmprestimo.Prepare;

  QyCademprestimo.ParamByName(‘inicio’).asDate :=dtInicio.Date;

  QyCadEmprestimo.ParamByname(‘fim’).asDate :=dtFim.Date;

  QyCadEmprestimo.Open;

  Screen.Cursor:=crDefault;

end;

 

Primeiramente, a propriedade ‘Cursor’ do objeto TScreen é alterada para crHourGlass. Isso mudará o cursor do mouse para a forma de ampulheta, enquanto a query estiver sendo processada. Logo após, o objeto Query é manipulado para apresentar os resultados. Vejamos:

 

QyCadEmprestimo.Close;

 

Aqui o objeto é fechado – já que não podemos alterar sua estrutura com o query aberto.

 

QyCadEmprestimo.Prepare;

 

O método Prepare garante ao BDE (ou ao servidor) a prioridade de execução da Query, improvisando performance.

 

QyCadEmprestimo.ParamByName(‘inicio’).asDate:=dtInicio.Date;

 

Aqui a data selecionada no objeto dtInicio é passada à variável inicio, criada anteriormente dentro da propriedade SQL do objeto Query1. Repare que as variáveis criadas dentro da propriedade SQL podem ser acessadas através da propriedade ParamByName do objeto TQuery.

 

QyCadEmprestimo.ParamByName(‘fim’).asDate:=dtFim.Date;

 

Como em inicio, o valor do objeto ‘dtFim’ é passado à variável Fim.

 

QyCadEmprestimo.Open;

Screen.Cursor:=crDefault;

 

E finalmente, a Query é aberta e o cursor do mouse é restaurado ao seu estado normal.

Toda vez que o usuário selecionar uma nova data no objeto ‘dtInicio’, a Query será reprocessada com o novo valor e a grade irá exibir os novos dados. Isto também deve valer para o objeto ‘dtFim’. Não precisamos repetir o código visto acima no evento OnCloseUp do objeto ‘dtFim’, basta selecionar o evento dtInicioCloseUp, no combobox do evento OnCloseUp do objeto ‘dtFim’.

 

 

Precisamos também abrir a query no momento em que o formulário for chamado. Façamos isso no evento OnShow:

 

procedure TfrmConsEmprestimos.FormShow(Sender: TObject);

begin

  dtFim.Date:=Date;

  dtInicio.Date:=date;

  dtInicioCloseUp(nil);

end;

 

A rotina é bem simples: apenas inicializa-se os objetos dtinicio e dtfim com a data atual e a seguir, a procedure dtInicioCloseUp é executada. Repare que a procedure dtInicioCloseUp nada mais é do que o evento OnCloseUp do objeto ‘dtInicio’.

Pronto! Precisamos somente codificar o botão Ok, pois este precisa passar o valor do empréstimo selecionado à variável código:

 

procedure TfrmConsEmprestimos.BitBtn1Click(Sender:TObject);

begin

if QyCadEmprestimo.RecordCount>0 then

  Codigo:=Query1.Fieldbyname

   (‘Codigo’).asInteger;

  Close;

end;

 

A váriavel Codigo receberá o valor do campo código do registro atualmente selecionado. Para que este formulário possa acessar a váriavel Código declarada no formulário de empréstimos, devemos declarar a unit do ‘Cadastro de Empréstimos’ na seção uses do ‘Form de consulta’, como segue:

 

uses

Emprestimos, Windows,

Messages, SysUtils, Classes,

Graphics, Controls, Forms,

Dialogs, StdCtrls, Buttons,

Db, DBTables, Grids, DBGrids,

ComCtrls;

 

Ok” agora os botões de alteração e exclusão do cadastro de empréstimo podem ser utilizados corretamente!

 

Incluindo Livros para o Empréstimo.

Resta-nos ainda uma implementação no formulário de empréstimos: a inclusão dos livros que serão emprestados. O usuário irá selecionar os livros a serem emprestados clicando no botão ‘Incluir livros’,

localizado abaixo da grade, como mostra a figura 6. Ao clicar ,o sistema irá apresentar uma tela exibindo todos os livros que estão disponíveis, ou seja, que não estão emprestados. A teça ficará de acordo com a figura 6.

Iniciaremos criando um novo form, através do menu Fileà New à Form.

Salve o projeto e chame este arquivo de ‘ConsLivrosNaoEmprestados.pas’. Altere as propriedades do formulário de acordo com a tabela a seguir:

 

Name FrmConsLivrosNao

Emprestados

BorderStyle bsDialog

Position poScreenCenter

Caption Livros Disponíveis

 

Figura 6

 

Coloque agora os objetos de acordo com a figura 6, e configure suas propriedades como segue:

 

Database Name Biblio

Name Query1

Tabela

Dataset Query1

Name DataSource1

 

DataSource DataSource1

Name DBGrid1

 

 

Name Bitbtn1

Kind kdOK

ModalResult mbOK

 

Name Bitbtn2

Kind kdCancel

ModalResult mbCancel

 

A ‘Query’ deste formulário deverá apresentar os livros que estão disponíveis para serem emprestados. O código SQL para esta operação é um pouco mais complexo, confira abaixo:

 

SELECT CodLivro, Titulo,

Serie

FROM Exemplar, Livro

WHERE Exemplar.CodLivro =Livro.Codigo

AND

Cast(codLivro AS Char(10)) + Cast(Serie AS Char(10))

NOT IN (SELECT Cast(codlivro AS Char(10)) +Cast(serie AS Char(10))

FROM LivrosEmp,Emprestimo

WHERE

LivrosEmp.codEmp=Emprestimo.Codigo

and

Emprestimo.dataDevolucaois null)

 

Vamos por partes: primeiramente, selecionamos todos os livros e seus respectivos exemplares, através do SQL:

 

SELECT CodLivro, Titulo,Serie

FROM Exemplar, Livros

WHERE Exemplar.CodLivro =Livros.Codigo

 

Este é um SQL básico – simplesmente unimos duas tabelas relacionadas, a de livros e de exemplares. Somente exemplares que não estão envolvidos em nenhum empréstimo pode ser visualizado. Veja o SQL abaixo:

 

SELECT *

FROM LivrosEmp, Emprestimo

WHERE (LivrosEmp.CodEmp = Emprestimo.Codigo)

AND

(Emprestimo.DataDevolucao is null).

 

Neste ‘SQL’, seriam mostrados todos os empréstimos que estão sem devolução, e conseqüentemente, todos os livros que estão emprestados – já que a tabela de livrosemp também faz parte da query. Vamos fazer um refinamento nesta Query:

 

SELECT Distinct

LivroEmp.CodLivro

FROM LivrosEmp, Emprestimo

WHERE LivrosEmp.CodEmp =Emprestimo.Codigo

AND Emprestimo.DataDevolucao is null

 

Aqui seria mostrado somente o código dos livros não devolvidos, repare que o comando Distinct elimina ocorrências iguais da mesma linha no resultado da Query. Teríamos, portanto, apenas os códigos dos livros não devolvidos, e que não podem ser emprestados.

O que devemos fazer é simples – filtramos apenas os livros que não pertencem ao resultado obtido nesta consulta.

Este filtro pode ser feito com o comando in – este filtra os registros de uma query que estão contidos em um conjunto. Este conjunto, obviamente, pode ser outra ‘Query’. Observe o código abaixo:

 

Select

CodLivro, titulo, serie

From Exemplar, Livros

Where

Exemplar.Codlivro =livros.codigo

And

Exemplar.CodLivro + Exemplar.Serie

NOT IN ( Select LivrosEmp.CodLivro + LivrosEmp.Serie

From

LivrosEmp,Emprestimo

Where

LivrosEmp.codEmp = Emprestimo.Codigo

And

Emprestimo.DataDevolucao is null )

 

Repare a junção das duas querys:

Todos os livros são selecionados, e depois são filtrados com o comando NOT IN, que compara se eles não estão dentro do conjunto passado como parâmetro, que no caso é a query que retorna todos os livros emprestados.

Ainda temos um problema, na linha mostrada abaixo:

 

... Exemplar.CodLivro + Exemplar.Serie NOT IN...

 

O Identificador do livro é formado pelos campos codigo e número de série. Por exemplo, podemos ter o livro de código 1, com as respectivas séries 1, 2 e 3. Com isso temos o livro 1-1, o livro 1-2, e o livro 1-3 que não se repetirão. Na linha mostrada acima, não será feita uma concatenação dos valores, e sim uma soma, retornando os livros: 2, 3 e 4. Para que possamos concatenar os dois valores, precisamos primeiro convertê-los para caracter. Isto é feito com o comando CAST:

 

CAST(Exemplar.CodLivro AS CHAR(10))

Nossa ‘Query’, então, ficaria da seguinte forma:

Select

CodLivro, titulo, serie

From

Exemplar, Livros

Where

Exemplar.Codlivro = ivros.codigo

And

CAST(Exemplar.CodLivro AS CHAR(10)) + CAST(Exemplar.Serie AS CHAR(10))

NOT IN( Select CAST(LivrosEmp.CodLivro AS Char(10)) CAST(LivrosEmp.Serie AS Char(10))

From LivrosEmp, Emprestimo

Where

LivrosEmp.codEmp =Emprestimo.Codigo

And

Emprestimo. DataDevolucao is null )

 

Precisamos agora abrir a ‘Query’, no evento OnShow do formulário:

 

procedure TfrmConsLivrosNaoEmprestados.FormShow(Sender:TObject);

begin

  query1.close;

  query1.prepare;

  query1.open;

end;

 

E no evento OnClick do botão ‘Ok’, precisamos passar o código e a série do livro selecionado na grade para as variáveis Codigo e NroSerie a serem usadas pelo formulário de empréstimos:

 

procedure

TFrmConsLivrosDevolvidos.BitBtn1Click(Sender:TObject);

begin

  Codigo:=Query1.Fieldbyname(‘CodLivro’).asInteger;

  NroSerie:=Query1.Fieldbyname(‘Serie’).asInteger;

  Close;

end;

 

Para que a variável Codigo e NroSerie possam ser acessadas neste formulário, devemos declarar o Form de empréstimos na cláusula uses de Consulta de Livros:

 

uses

Emprestimos, Windows,

Messages, SysUtils, Classes,

Graphics, Controls, Forms,

Dialogs, StdCtrls, Buttons,

Db, DBTables, Grids, DBGrids;

 

Estamos prontos para criar a inclusão de livros no formulário de empréstimos:

Abra novamente o formulário de empréstimo(FrmCadEmprestimos), e selecione o evento OnClick do botão ‘btIncluirLivros’. O código referente segue abaixo:

 

procedure TfrmCadEmprestimos.btIncluirLivrosClick(Sender:TObject);

begin

  Codigo:=-1;

  FrmConsLivrosNaoEmprestados.Showmodal;

  If codigo>0 then

   begin

     TbLivrosemp.Append;

     TbLivrosEmp.FieldByName(‘CodLivro’).asInteger := Codigo;

     TbLivrosEmp.Fieldbyname(‘Serie’).asInteger:=NroSerie;

     TbLivrosEmp.Post;

 end;

end;

 

Simples – o formulário de livros não emprestados é exibido; se a variável Codigo for maior que 0 (zero), ou seja, se o usuário selecionou algum livro, então este é incluído na tabela de livros emprestados (LivrosEmp.db). Vejamos agora o botão ‘btExcluirLivros’:

 

procedure TfrmCadEmprestimos.btExcluirLivrosClick(Sender:TObject);

begin

if tbLivrosEmp.RecordCount>0

then

tbLivrosEmp.Delete;

end;

 

O botão ExcluirLivros simplesmente executa o método delete na tabela de livrosemp. Repare que a Propriedade Record Count do objeto Ttable é verificada, para saber se há algum registro nesta tabela.

 

Retoques Finais

O formulário já está todo operacional. Devemos apenas dar alguns retoques, como validar os dados de entrada e fornecer máscaras, no campo data por exemplo. Esses passos não serão ilustrados aqui, pois vimos detalhadamente como fazer nas etapas anteriores. A única alteração na qual considero interessante neste momento, é dar uma melhor apresentação à grade de livros. Atualmente o usuário

visualizaria os livros como mostra a figura 7.

 

Figura 7. Grade sem formatação

 

Uma forma não muito bonita e menos prática de se visualizar os livros. Vamos começar incluindo o nome do livro nesta grade, pois este é um detalhe imprescindível. Para isto, devemos criar um objeto TField, que será do tipo ‘LookUp’. Os objetos TField são aqueles que criamos ao selecionar os campos dando um duplo clique sobre o objeto TTable. Vamos então, criar objetos TField para os campos já existentes na tabela livrosEmp:

Dê um clique duplo sobre o objeto tbLivrosEmp. Na janela subseqüente, clique com o botão direito do mouse e selecione ‘Add all fields’. Cada item incluído na lista é um objeto TField, e representa o respectivo campo na tabela.

Para exibir o nome do livro, precisamos criar um TField que apontará para o campo Titulo na tabela de livros. Para isto, clique com o botão direito sobre a lista de campos, e selecione a opção New Field... – A janela será mostrada de acordo com a figura 8.

 

Figura 8 – janella ‘New Field’

 

Preencha corretamente os campos Field Properties, de acordo com a tabela abaixo:

 

Name TituloLivro

Type String

Size 50

 

No RadioGroup FieldType, selecione a opção ‘Lookup’, pois nosso objeto será um TfieldLookUp, este tipo de Tfield procura informações em outra tabela, por isso seu nome LookUp.

Na grade Lookup Definition, preencha as informações de acordo com a tabela abaixo:

 

KeyFields – O campo que servirá de link com a tabela externa – no caso, CodLivro.

DataSet -A tabela de origem dos dados – TbLivro

LookUpKeys – O campo da tabela de origem que unirá as duas tabelas – Codigo

Result Field – O que o usuário visualizará – TituloTabela 29: Propriedades do novo campo

Clique em Ok e execute a aplicação para testar o novo campo – Titulo do livro.

É interessante também que escondamos o campo CodEmp. Este campo serve de ‘Link’ com a tabela de empréstimos e nada acrescentará ao usuário.

Para escondê-lo, dê um duplo clique sobre o objeto DBGrid. A janela Editing DBGrid1.Columns será mostrada. Para termos acesso as coluna do DBGrid, deve-se deixar a tabela tbLivrosEmp aberta – basta mudar sua propriedade Active para True.

Na janela Editing Columns, clique no botão ‘Add all fields’, para que as colunas sejam mostradas na lista. Selecione ‘CodEmp’ e clique sobre obotão ‘Delete’, para que esta coluna não seja mostrada.

 

Nota: Existem propriedades interessantes para as colunas do DbGrid – Pode-se, por exemplo, modificar o título das colunas, a cor ou o tamanho. Você pode alterar as propriedades para criar combinações criativas para as colunas do objeto TDBGrid.

 

E o último passo é setar a propriedade ReadyOnly do GRID para True, inibindo as alterações manuais na grade, e obrigando o nosso usuário a clicar nos botões ‘Incluir livro’ e ‘Cancelar livro’.

 

Finalizando o Form de Empréstimos

Nosso formulário de empréstimos está terminado! Como último passo, podemos incluir o fechamento de todas as tabelas e do objeto Database no evento OnClose do nosso form, como mostra o código abaixo:

 

procedure TFrmCadEmprestimos.FormClose (Sender: TObject;var Action: TCloseAction);

begin

  tbCadEmprestimo.Close;

  TbLivrosEmp.close;

  TbLivro.Close;

  TbLeitor.Close;

  Database1.Connected:=False;

end;

 

Utilizaremos pela primeira vez o evento OnCloseQuery:

 

procedure TFrmCadEmprestimos.FormCloseQuery(Sender: TObject;

  var CanClose: Boolean);

begin

CanClose:=True;

if

(tbCadEmprestimo.State=dsInsert)

or

(tbCadEmprestimo.State=dsEdit)

then

if

Application.MessageBox('Deseja cancelar asalterações?','Biblio',MB_YESNO)= ID_YES then

btCancelarClick(nil)

else

CanClose:=False;

end;

 

O evento OnCloseQuery permite que a ação de fechamento de um formulário seja cancelada. No caso, a rotina verifica se a tabela está em modo de inclusão ou alteração. Caso positivo,uma mensagem é exibida, perguntando ao usuário se quer cancelar as alterações realizadas. O parâmetro CanClose indica se o formulário será fechado ou não. Se passarmos o valor False para o parâmetro CanClose, o formulário não será fechado, mesmo que o usuário tenha clicado no botão de controle da janela.

 

Conclusão

Esta foi a parte mais longa de nosso curso. Apesar da complexidade aqui ter sido maior do que na etapas anteriores, vimos que no geral uma aplicação pode ser rapidamente construída com o Delphi. Nesta parte conhecemos novos recursos, como o controle de transações, a passagem de parâmetros para consultas SQL, o uso do componente TDBLookUpComboBox e campos do LookUp dentro de um DBGrid, assim como consultas SQL um pouco mais complexas do que nas edições anteriores. Na próxima edição continuaremos com o último dos cadastros, o cadastro de devoluções, para iniciarmos a construção de nossos relatórios. Espero que tenha sido claro em minhas observações, e que estas sejam de fácil entendimento para vocês. Dúvidas, críticas ou sugestões sobre este sistema podem ser enviadas para admin@clubedelphi.com.br