Você está na página 1de 26

FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

Artigos Delphi FireMonkey e FireDAC: Construindo uma aplicação completa – Parte II

Demais posts desta série:

Na primeira parte desta série de artigos que mostra como construir um sistema completo de ordens de serviços no
Delphi utilizando o FireMonkey, o MySQL e a engine de acesso a dados FireDAC foi construída a base de dados e
também foram implementadas as janelas para listagem dos registros. Nesta segunda e última parte o foco será dado à
construção das janelas de cadastro e pesquisa. As próximas seções mostram o desenvolvimento completo do so3ware.

O sistema possui até agora sete formulários e seis Data Modules, que são automaticamente instanciados quando o
projeto é executado. Esse fato pode ser observado na Figura 1 na janela de configurações do projeto
( ), na qual é possível observar do lado esquerdo da figura que todos os arquivos estão localizados na
parte . Apesar de essa ser uma configuração padrão do Delphi e não requerer nenhum código
adicional para instanciar os objetos, dependendo da quantidade de arquivos presentes no sistema o carregamento
inicial pode ficar lento, visto que todos os componentes serão criados assim que o projeto for executado. Uma maneira
mais profissional é criá-los manualmente de acordo com a necessidade do usuário, como por exemplo: quando o
botão para a listagem de clientes for clicado a respectiva tela juntamente com o Data Module serão instanciados em
memória. Por outro lado, quando a janela for fechada ambos os arquivos devem ser liberados da memória. Com
relação ao formulário este fato já está acontecendo, visto que é utilizada a variável para gerenciar a

1 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

única janela aberta em tela juntamente com o comando no método .

Apesar dos formulários de cadastro e movimentação já estarem sendo criados em tempo de execução, é necessário
que eles fiquem localizados na parte à direita da Figura 1, caso contrário eles serão instanciados na
inicialização do sistema e também quando o usuário abrir uma janela. Por meio dessa configuração, os arquivos não
serão criados automaticamente e ficarão disponíveis para que o desenvolvedor os instancie quando necessário. Os
únicos arquivos que devem ser criados automaticamente é o menu principal que será a primeira janela a ser exibida e
o Data Module que contém a conexão com a base de dados ( ).

Nota: Apesar dessa abordagem de criação manual dos arquivos requerer mais linhas de código, ela
poderá evitar demoras no carregamento inicial do sistema e também consumir menos memória quando
estiver em execução. Como estamos desenvolvendo utilizando o FireMonkey, além das aplicações

funcionarem em desktop (Windows e OS X) elas também podem ser portadas para o ambiente mobile
(Android e iOS), nos quais a memória é reduzida devido a limitações de hardware da maioria dos
dispositivos.

Apesar de que com a codificação atual os formulários já estão sendo criados dinamicamente, caso o so3ware seja
executado será gerado erro pelo fato de ser necessário ainda programar a instanciação manual dos Data Modules
correspondentes a cada janela. A Listagem 1 apresenta esse código, no qual foram programados os eventos
e . Na linha 04 o Data Module é instanciado quando o formulário é criado, enquanto que na
linha 09 o mesmo Data Module é liberado da memória utilizando o método , ambos para a tela de pesquisa
de ordens de serviços. O mesmo processo ocorre para as demais janelas, ou seja: entre as linhas 11 e 21 encontra-se o
código correspondente a pesquisa de clientes, entre as linhas 22 e 32 das formas de pagamento, entre 33 e 43 do
cadastro de serviços e por fim, entre as linhas 44 e 54 é código relacionado ao cadastro de veículos.

Listagem 1. Criação manual dos Data Modules.

01 procedure TfrmOrdemServicoListagem.FormCreate(Sender: TObject);


02 begin
03 inherited;
04 dtmOrdemServico := TdtmOrdemServico.Create(Self);
05 end;
06 procedure TfrmOrdemServicoListagem.FormDestroy(Sender: TObject);
07 begin
08 inherited;
09 dtmOrdemServico.DisposeOf;
10 end;
11 procedure TfrmClienteListagem.FormCreate(Sender: TObject);
12 begin
13 inherited;
14 dtmCliente := TdtmCliente.Create(Self);
15 dtmCliente.qryCliente.Open();
16 end;
17 procedure TfrmClienteListagem.FormDestroy(Sender: TObject);
18 begin
19 inherited;
20 dtmCliente.DisposeOf;
21 end;
22 procedure TfrmFormaPagamentoListagem.FormCreate(Sender: TObject);
23 begin

2 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

26 dtmFormaPagamento.qryFormaPagamento.Open();
27 end;
28 procedure TfrmFormaPagamentoListagem.FormDestroy(Sender: TObject);
29 begin

31 dtmFormaPagamento.DisposeOf;
32 end;
33 procedure TfrmServicoListagem.FormCreate(Sender: TObject);
34 begin
35 inherited;
36 dtmServico := TdtmServico.Create(Self);
37 dtmServico.qryServico.Open();
38 end;
39 procedure TfrmServicoListagem.FormDestroy(Sender: TObject);
40 begin
41 inherited;
42 dtmServico.DisposeOf;
43 end;
44 procedure TfrmVeiculoListagem.FormCreate(Sender: TObject);
45 begin
46 inherited;
47 dtmVeiculo := TdtmVeiculo.Create(Self);
48 dtmVeiculo.qryVeiculo.Open();
49 end;
50 procedure TfrmVeiculoListagem.FormDestroy(Sender: TObject);
51 begin
52 inherited;
53 dtmVeiculo.DisposeOf;
54 end;

Na Figura 2 são apresentadas as janelas que serão utilizadas para a construção dos cadastros básicos, sendo indicado o
cadastro de serviços. Primeiramente o usuário deverá abrir a janela com a listagem dos registros (esquerda da figura),
e após clicar no botão “incluir” ou “alterar” será aberta a janela de cadastro para o preenchimento e/ou visualização
dos dados completos do registro. Se a opção escolhida for a inclusão, os campos de texto para digitação dos dados
estarão em branco. Por outro lado, se um registro for selecionado na grade de dados e logo após o botão alterar for
clicado, as informações correspondentes a este registro serão carregadas nessa janela para edição (conforme
mostrado para o serviço “ajuste no freio” na figura). É também possível observar que está presente um Data Module, o
qual terá a função de carregar, incluir e editar os dados do cadastro. Nesta figura está sendo mostrado o cadastro de
serviços, e a seguir a codificação necessária será abordada em detalhes.

Nota: Como pode ser observado na Figura 2, o Data Module possui somente um componente TFDQuery
para executar as operações de consulta, inclusão e edição dos registros. Em um primeiro momento,

poderíamos chegar à conclusão de que seria melhor adicioná-lo diretamente no formulário de cadastro
devido à pouca complexidade destas operações. Porém, é uma boa prática criar um Data Module para
que os componentes de acesso a dados fiquem centralizados nele, e essa estrutura contribui para a

organização do projeto. Neste contexto, caso o so3ware venha a crescer e se tornar mais complexo o
gerenciamento dos arquivos será facilitado.

O primeiro passo para a construção do cadastro de serviços é a criação de um novo formulário (File>New>Multi-

3 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

apresentada na Figura 3 e é composta por uma na parte superior e mais dois dentro
dela ( ) com a propriedade configurada para . Há também um

) que serão responsáveis pela gravação dos registros (tanto inclusão quanto alteração) e pelo
cancelamento (desfazer) dos dados informados pelo usuário, respectivamente. Conforme pôde ser observado, foram
utilizados os recursos automáticos do Delphi, ou seja, não serão desenvolvidas linhas de código adicionais para essas
duas funções. Por fim, os dois botões devem ter sua propriedade atribuídas a essas novas ações, bem como
deve ser adicionada uma variável chamada na seção pública do formulário, conforme mostrado na Listagem 2.
Essa variável será utilizada posteriormente para o gerenciamento da abertura da janela, afim de indicar que se trata de
uma inserção ou de uma alteração.

Nota: É importante salientar que todos os formulários e Data Modules que serão criados a partir de agora
devem ser configurados para serem criados de forma manual, conforme abordado anteriormente.

Listagem 2. Variável para gerenciar a inclusão e alteração de registros.

01 public
02 FId: Integer;
03 end;

Em seguida deve ser criado um novo Data Module (File>New>Other>Delphi Files>Data Module) chamado
com um componente ( ), conforme apresentado na Figura 2. Esse novo
arquivo deve possuir acesso ao Data Module de conexão à base de dados ( ) por meio do comando
, bem como sua propriedade configurada para que diz respeito ao componente
que efetivamente faz a ligação de nossa aplicação com a base de dados das ordens de serviços.
Adicionalmente, a propriedade desta query deve possuir o seguinte comando SQL:

select idservico, nome, observação from c_servico

Após a digitação dessa instrução, é necessário acessar o do componente e adicionar todos os campos
no formato (botão direito>Fields Editor>botão direito>Add all fields) para que possamos realizar a vinculação
deles com os componentes visuais utilizando Live Bindings. Conforme mencionado anteriormente, essa query será
responsável pelo carregamento, inclusão e alteração dos registros, fazendo uso das ações padrões definidas no
componente .

Um novo formulário deve ser criado (File>New>Other>Inheritable Items) herdando de para que
possamos utilizar tanto os recursos visuais quanto a codificação inserida no formulário base de cadastro. Este novo
formulário deve ser chamado de e possuir visual semelhante ao mostrado na Figura 2, ou seja,
dois (nome e observações), um para armazenar o nome do serviço ( ) e um para
armazenar as observações ( ). Por meio do comando é necessário adicionar a importação
para o Data Module recém criado ( ) para que tenhamos acesso aos campos . A Figura 4

4 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

apresenta a janela para efetuar o Live Bindings entre os campos da query e os campos visuais, podendo-se observar
que o campo está vinculado ao e o campo está ligado ao . Desta forma, os

codificações adicionais. Outra configuração imprescindível é ligar a propriedade das ações e


do ao componente que é adicionado automaticamente no formulário quando
o Live Bindings é feito. Esse processo é necessário para que as ações padrões estejam vinculadas a um e
efetivamente consigam realizar a gravação e o cancelamento quando os respectivos botões forem acionados pelo
usuário.

Na Listagem 3 é apresentado o código que deve ser inserido no evento da janela de cadastro dos serviços, na
qual pode-se observar na linha 04 que o Data Module é instanciado para que tenhamos acesso aos
dados em tempo de execução. É importante enfatizar que esse código é necessário porque todos os arquivos estão
configurados para serem criados manualmente.

Nota: O código presente nas listagens diz respeito à herança com as janelas básicas e indica

que todo o código que está presente na classe pai será também executado na classe filha. Caso não seja

necessário executar um comportamento da superclasse, essa linha de código pode ser retirada.

Listagem 3. Evento OnCreate da janela de serviços.

01 procedure TfrmServicoCadastro.FormCreate(Sender: TObject);


02 begin
03 inherited;
04 dtmServicoCadastro := TdtmServicoCadastro.Create(Self);
05 end;

Na Listagem 4 é possível observar o código que deve ser definido no evento da janela de serviços, o qual é
responsável por gerenciar o comportamento da janela caso esteja sendo feita uma inclusão (linha 06 até 09) ou uma
alteração (linha 12 até 15). Na linha 04 é feita uma verificação na variável (que pertence à superclasse
definida na Listagem 2) para buscar um dos dois estados, ou seja, o valor 0 (zero) indica que se
trata de uma inclusão e na linha 07 é adicionado um comando na query para que não seja retornado nenhum
registro que possa estar cadastrado na tabela (comando ). Logo após, na linha 08 a query é aberta e na linha 09 é
executado o comando que prepara a query para receber novos registros. Em suma, esse código faz com que a
janela de cadastro seja aberta sem nenhum registro ativo. Por outro lado, caso o valor da variável for diferente de
0 (zero) o fluxo de execução do programa executará a partir da linha 10, ou seja, quando se trata de uma edição de
registro. Similarmente ao código anterior, na linha 13 é definido um comando ; porém é passado como
parâmetro o valor da variável para que o registro correspondente a esse identificador seja carregado no .
Logo após, na linha 14 a query é aberta e logo em seguida na linha 15 ela é colocada em modo de edição para que o
usuário realize as alterações necessárias. Em resumo, quando um registro é selecionado na grade de dados da janela
de listagem de serviços ( ) e o botão “alterar” for clicado, a chave primária desse registro é passada

5 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

Listagem 4. Evento OnShow da janela de serviços.

02 begin
03 inherited;
04 if FId = 0 then
05 begin
06 dtmServicoCadastro.qryServico.Close;
07 dtmServicoCadastro.qryServico.SQL.Add(' where idservico is null');
08 dtmServicoCadastro.qryServico.Open;
09 dtmServicoCadastro.qryServico.Insert;
10 end else
11 begin
12 dtmServicoCadastro.qryServico.Close;
13 dtmServicoCadastro.qryServico.SQL.Add(' where idservico = ' + QuotedStr(IntToStr(FId)));
14 dtmServicoCadastro.qryServico.Open;
15 dtmServicoCadastro.qryServico.Edit;
16 end;
17 end;

Até este ponto foram vistas as operações referentes à janela de cadastro, e a partir de agora veremos as configurações
e codificações necessárias na janela de listagem de serviços ( ). Na Listagem 5 apresenta-se a
codificação que corresponde à abertura da janela de cadastro, e para isso, inicialmente é definida a variável
na seção do formulário conforme mostrado na linha 02 (necessário adicionar a cláusula
para esse arquivo na seção do formulário). A partir da linha 05 é mostrado o código que deve ser
informado no evento do botão para inclusão ( ), que primeiramente cria uma nova instância da
janela de cadastro na linha 08, atribui 0 (zero) para a variável para que seja executado o código entre as linhas 06 e
09 da Listagem 4 explanada anteriormente. Na linha 10 é invocado o método que efetivamente abre a janela
de cadastro e deixa o sistema “preso” nesta tela até que o formulário seja fechado pelo usuário. Desta forma, o código
das linhas 11 e 12 somente é executado depois que o usuário terminar a inserção de um novo registro, para que o
formulário seja liberado da memória (linha 11) e o dataset atualizado (linha 12).

Listagem 5. Inclusão de novo serviço.

01 private
02 FJanelaCadastro: TfrmServicoCadastro;
03 public
04 end;
05 procedure TfrmServicoListagem.btnIncluirClick(Sender: TObject);
06 begin
07 inherited;
08 FJanelaCadastro := TfrmServicoCadastro.Create(Self);
09 FJanelaCadastro.FId := 0;
10 FJanelaCadastro.ShowModal;
11 FJanelaCadastro.DisposeOf;
12 BindSourceDB1.DataSet.Refresh;
13 end;

A Listagem 6 é bastante similar, com a diferença principal de que na linha 07 a variável recebe o registro que está
selecionado no componente . Note que para acessar o registro selecionado é feita referência para o
e não para a grade de dados, e isso ocorre devido ao fato de que como a ligação da grade com o dataset
foi feita por meio de Live Bindings o próprio possui acesso ao registro selecionado no componente
visual. Na linha 04 é feita uma validação para certificar-se de que existe registro selecionado, e caso negativo o usuário
recebe a mensagem da linha 13.

Listagem 6. Edição de novo serviço.

6 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

03 inherited;
04 if not BindSourceDB1.DataSet.IsEmpty then
05 begin
06 FJanelaCadastro := TfrmServicoCadastro Create(Self);

08 FJanelaCadastro.ShowModal;
09 FJanelaCadastro.DisposeOf;
10 BindSourceDB1.DataSet.Refresh;
11 end
12 else
13 ShowMessage('Nenhum registro está selecionado.');
14 end;

O último passo para a finalização completa do cadastro de serviços é a programação do procedimento para a exclusão
de registros, que é mostrado na Listagem 7 no evento do botão excluir ( ) da janela de listagem
( ). Na linha 04 encontra-se o código que mostra uma mensagem de confirmação ao usuário, que
necessita que os seguintes namespaces sejam adicionados na seção da janela: e . Caso
a resposta do usuário seja afirmativa ( ), na linha 06 o registro é excluído e na linha 07 o dataset é atualizado para
não mostrar mais o registro excluído em tela.

Listagem 7. Exclusão de serviços.

01 procedure TfrmServicoListagem.btnExcluirClick(Sender: TObject);


02 begin
03 inherited;
04 if Application.MessageBox('Confirma a exclusão do registro selecionado?',
'Confirmação', MB_YESNO + MB_ICONQUESTION) = IDYES then
05 begin
06 BindSourceDB1.DataSet.Delete;
07 BindSourceDB1.DataSet.Refresh;
08 end;
09 end;

A seção anterior mostrou em detalhes o desenvolvimento dos arquivos para a implementação do cadastro de serviços,
que envolveu as seguintes etapas: criação (i) do data module, (ii) da janela de cadastro e (iii) a codificação da janela de
pesquisa. Com relação aos outros cadastros básicos, ainda falta desenvolver o cadastro das formas de pagamento, dos
veículos e dos clientes. Com isso, o objetivo desta seção é mostrar resumidamente as respectivas janelas juntamente
com a codificação necessária para cada uma (o evento para exclusão não será mostrado pois o código é o mesmo para
todas as janelas, porém ele deve ser informado no respectivo evento). A Figura 5 apresenta a janela para o cadastro das
formas de pagamento, na qual é possível observar à esquerda a listagem dos registros, no centro a janela de cadastro
com um registro selecionado para alteração e por fim, na direita encontra-se o data module correspondente.

A seguinte instrução deve ser adicionada na propriedade do componente , que conforme


definido anteriormente, terá também a função de gerenciar o carregamento, inclusão e alteração de registros:

select idforma_pagamento, nome, observacao from c_forma_pagamento

7 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

(linhas 06 até 22). Eles são muito similares aos que foram programados anteriormente, ou seja, o primeiro é
responsável pela criação do data module de cadastro enquanto que o segundo tem o objetivo de gerenciar como será a

Listagem 8. Codificação na janela de cadastro de formas de pagamento.

01 procedure TfrmFormaPagamentoCadastro.FormCreate(Sender: TObject);


02 begin
03 inherited;
04 dtmFormaPagamentoCadastro := TdtmFormaPagamentoCadastro.Create(Self);
05 end;
06 procedure TfrmFormaPagamentoCadastro.FormShow(Sender: TObject);
07 begin
08 inherited;
09 if FId = 0 then
10 begin
11 dtmFormaPagamentoCadastro.qryFormaPagamento.Close;
12 dtmFormaPagamentoCadastro.qryFormaPagamento.SQL.Add
(' where idforma_pagamento is null');
13 dtmFormaPagamentoCadastro.qryFormaPagamento.Open;
14 dtmFormaPagamentoCadastro.qryFormaPagamento.Insert;
15 end else
16 begin
17 dtmFormaPagamentoCadastro.qryFormaPagamento.Close;
18 dtmFormaPagamentoCadastro.qryFormaPagamento.SQL.Add
(' where idforma_pagamento = ' + QuotedStr(IntToStr(FId)));
19 dtmFormaPagamentoCadastro.qryFormaPagamento.Open;
20 dtmFormaPagamentoCadastro.qryFormaPagamento.Edit;
21 end;
22 end;

Na Listagem 9 é apresentado o código responsável pela definição da janela de cadastro (linhas 01 até 04), no método
para inclusão de novos registros (linhas 05 até 13) e por fim, do método para alteração de registros (linhas 14 até 27).
Assim como na listagem anterior, esse código é similar ao apresentado para a janela de cadastro de serviços e difere
somente no que diz respeito aos tipos dos objetos criados e dados acessados.

Listagem 9. Codificação na janela de listagem de formas de pagamento.

01 private
02 FJanelaCadastro: TfrmFormaPagamentoCadastro;
03 public
04 end;
05 procedure TfrmFormaPagamentoListagem.btnIncluirClick(Sender: TObject);
06 begin
07 inherited;
08 FJanelaCadastro := TfrmFormaPagamentoCadastro.Create(Self);
09 FJanelaCadastro.FId := 0;
10 FJanelaCadastro.ShowModal;
11 FJanelaCadastro.DisposeOf;
12 BindSourceDB1.DataSet.Refresh;
13 end;
14 procedure TfrmFormaPagamentoListagem.btnAlterarClick(Sender: TObject);
15 begin
16 inherited;
17 if not BindSourceDB1.DataSet.IsEmpty then
18 begin
19 FJanelaCadastro := TfrmFormaPagamentoCadastro.Create(Self);
20 FJanelaCadastro.FId := BindSourceDB1.DataSet.FieldByName
('idforma_pagamento').AsInteger;
21 FJanelaCadastro.ShowModal;
22 FJanelaCadastro.DisposeOf;
23 BindSourceDB1.DataSet.Refresh;
24 end
25 else
26 ShowMessage('Nenhum registro está selecionado.');
27 end;

8 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

A Figura 6 apresenta a janela para o cadastro dos veículos, na qual é possível observar à esquerda a listagem dos
registros, no centro a janela de cadastro com um registro selecionado para alteração e por fim, na direita encontra-se o

Assim como para os outros cadastros, o seguinte comando deve ser adicionado na propriedade do componente
:

select idveiculo, placa, modelo, observacao from c_veiculo

É importante salientar que é uma boa prática buscar somente os campos mais importantes na janela de listagem de
registros, e posteriormente, quando o usuário selecionar um registro específico executar um comando SQL para trazer
todos os registros da tabela para edição. Esse fato ocorre com este comando, que além do , e
também carrega o campo que não havia sido carregado na janela de listagem, conforme pode ser
observado na Figura 6. Essa é uma técnica interessante para evitar tráfego na rede de campos desnecessários,
deixando assim o sistema mais rápido principalmente quando executado em dispositivos com recursos de hardware
limitados.

Similarmente aos outros cadastros, na Listagem 10 é apresentado o código que deve ser implementado para os eventos
(linhas 01 até 05) e (linhas 06 até 22), os quais são responsáveis pela instanciação do data module e
pelo gerenciamento da abertura da janela de modo a controlar se trata-se de uma inclusão ou alteração.

Listagem 10. Codificação na janela de cadastro de veículos.

01 procedure TfrmVeiculoCadastro.FormCreate(Sender: TObject);


02 begin
03 inherited;
04 dtmVeiculoCadastro := TdtmVeiculoCadastro.Create(Self);
05 end;
06 procedure TfrmVeiculoCadastro.FormShow(Sender: TObject);
07 begin
08 inherited;
09 if FId = 0 then
10 begin
11 dtmVeiculoCadastro.qryVeiculo.Close;
12 dtmVeiculoCadastro.qryVeiculo.SQL.Add(' where idveiculo is null');
13 dtmVeiculoCadastro.qryVeiculo.Open;
14 dtmVeiculoCadastro.qryVeiculo.Insert;
15 end else
16 begin
17 dtmVeiculoCadastro.qryVeiculo.Close;
18 dtmVeiculoCadastro.qryVeiculo.SQL.Add(' where idveiculo = ' +
QuotedStr(IntToStr(FId)));
19 dtmVeiculoCadastro.qryVeiculo.Open;
20 dtmVeiculoCadastro.qryVeiculo.Edit;
21 end;
22 end;

Na Listagem 11 é apresentado o código que deve ser implementado na janela de listagem de veículos
( ), a qual define a variável (linha 02), é criado o evento para inclusão de novos
registros de modo a abrir a janela vazia (linhas 05 até 13) e finalmente, entre as linhas 14 e 27 encontra-se o evento

9 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

Listagem 11. Codificação na janela de listagem de veículos.

02 FJanelaCadastro: TfrmVeiculoCadastro;
03 public
04 end;
05 procedure TfrmVeiculoListagem.btnAlterarClick(Sender: TObject);
06 begin
07 inherited;
08 if not BindSourceDB1.DataSet.IsEmpty then
09 begin
10 FJanelaCadastro := TfrmVeiculoCadastro.Create(Self);
11 FJanelaCadastro.FId := BindSourceDB1.DataSet.FieldByName('idveiculo').AsInteger;
12 FJanelaCadastro.ShowModal;
13 FJanelaCadastro.DisposeOf;
14 BindSourceDB1.DataSet.Refresh;
15 end
16 else
17 ShowMessage('Nenhum registro está selecionado.');
18 end;
19 procedure TfrmVeiculoListagem.btnIncluirClick(Sender: TObject);
20 begin
21 inherited;
22 FJanelaCadastro := TfrmVeiculoCadastro.Create(Self);
23 FJanelaCadastro.FId := 0;
24 FJanelaCadastro.ShowModal;
25 FJanelaCadastro.DisposeOf;
26 BindSourceDB1.DataSet.Refresh;
27 end;

A Figura 7 apresenta a janela para o cadastro dos clientes, na qual é possível observar à esquerda a listagem dos
registros, no centro a janela de cadastro com um registro selecionado para alteração e por fim, na direita encontra-se o
data module correspondente juntamente com os campos adicionados. Com exceção do campo data de
nascimento, que para o qual foi adicionado um componente , os outros componentes visuais foram
mantidos os mesmos, ou seja, e .

O seguinte comando deve ser adicionado na propriedade do componente :

select idcliente, nome, data_nascimento, cpf, rg, observacao from c_cliente

Da mesma maneira, é necessário criar os eventos para instanciação do data module de cadastro e também o evento
para o gerenciamento da abertura da janela, sendo representados nas linhas 01 até 05 e linhas 06 até 22,
respectivamente, da Listagem 12.

Listagem 12. Codificação na janela de cadastro de clientes.

01 procedure TfrmClienteCadastro.FormCreate(Sender: TObject);


02 begin
03 inherited;
04 dtmClienteCadastro := TdtmClienteCadastro.Create(Self);
05 end;
06 procedure TfrmClienteCadastro.FormShow(Sender: TObject);
07 begin
08 inherited;
09 if FId = 0 then

10 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

12 dtmClienteCadastro.qryCliente.SQL.Add(' where idcliente is null');


13 dtmClienteCadastro.qryCliente.Open;
14 dtmClienteCadastro.qryCliente.Insert;
15 end else

17 dtmClienteCadastro.qryCliente.Close;
18 dtmClienteCadastro.qryCliente.SQL.Add(' where idcliente = ' +
QuotedStr(IntToStr(FId)));
19 dtmClienteCadastro.qryCliente.Open;
20 dtmClienteCadastro.qryCliente.Edit;
21 end;
22 end;

Na Listagem 13 é apresentado o código que corresponde a criação da variável (linha 02), o evento
para aberta da janela em modo de inserção (linhas 05 até 13) e o evento para abertura da janela de cadastro em modo
de edição (linhas 14 até 27).

Listagem 13. Codificação na janela de listagem de clientes.

01 private
02 FJanelaCadastro: TfrmClienteCadastro;
03 public
04 end;
05 procedure TfrmClienteListagem.btnAlterarClick(Sender: TObject);
06 begin
07 inherited;
08 if not BindSourceDB1.DataSet.IsEmpty then
09 begin
10 FJanelaCadastro := TfrmClienteCadastro.Create(Self);
11 FJanelaCadastro.FId := BindSourceDB1.DataSet.FieldByName('idcliente').AsInteger;
12 FJanelaCadastro.ShowModal;
13 FJanelaCadastro.DisposeOf;
14 BindSourceDB1.DataSet.Refresh;
15 end
16 else
17 ShowMessage('Nenhum registro está selecionado.');
18 end;
19 procedure TfrmClienteListagem.btnIncluirClick(Sender: TObject);
20 begin
21 inherited;
22 FJanelaCadastro := TfrmClienteCadastro.Create(Self);
23 FJanelaCadastro.FId := 0;
24 FJanelaCadastro.ShowModal;
25 FJanelaCadastro.DisposeOf;
26 BindSourceDB1.DataSet.Refresh;
27 end;

Neste momento, todas as janelas de cadastro básicas já estão concluídas e agora partiremos para a construção do
formulário mais importante do nosso sistema que é o registro das ordens de serviços. Será mostrado como utilizar os
recursos de junções ( ) entre todas as tabelas construídas, bem como o funcionamento da janela mestre detalhe
para que seja possível incluir diversos serviços para uma mesma ordem. É possível observar na Figura 8 a mesma
estrutura das janelas de cadastro construídas anteriormente, porém, com a diferença de que existem três
( , e ) ao lado dos campos para preencher o
cliente, o veículo e a forma de pagamento; além de um para armazenar a data da ordem de serviço. Como
esses três campos são chaves estrangeiras para as respectivas tabelas, a lógica é que o usuário clique nesses botões e
seja aberta uma nova janela com uma listagem dos registros para que o usuário selecione o registro desejado. Nos
campos de texto serão armazenados os nomes e internamente será gravado o identificador (chave primária) das
tabelas para efetivamente gravar os registros na base de dados.

11 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

Na Figura 9 é mostrado o Live Bindings dos componentes visuais com os campos da query, na qual é possível
observar que as chaves estrangeiras não recebem nenhuma ligação com os campos visuais pelo motivo explanado
anteriormente; que é necessária uma codificação adicional para fazer essa vinculação/associação. Em resumo, essa
codificação será semelhante ao funcionamento de um componente da VCL (Visual Component Library)
para o Windows, ou seja, é mostrado o nome e gravado o identificador (chave primária).

Na Listagem 14 é possível observar o comando SQL que deve ser preenchido na propriedade do componente
. Note que diferentemente do script que é executado na janela de listagem dos registros, este
comando busca todos os campos da tabela mais o nome do cliente, a placa do veículo e a forma de
pagamento das outras tabelas. Conforme mencionado anteriormente, esses campos exibirão seus valores nos
componentes visuais enquanto que os identificadores ( , e ) serão utilizados
para efetuar o relacionamento entre as tabelas.

Listagem 14. Comando SQL para buscar as ordens de serviço.

01 select
02 ors.idordem_servico,
03 ors.idcliente,
04 ors.idveiculo,
05 ors.idforma_pagamento,
06 ors.valor_total,
07 ors.observacao,
08 cln.nome as cliente,
09 vlc.placa as veiculo,
10 fpg.nome as forma_pagamento,
11 ors.numero,
12 ors.data_ordem_servico
13 from
14 m_ordem_servico ors
15 inner join
16 c_cliente cln on ors.idcliente = cln.idcliente
17 inner join
18 c_veiculo vlc on ors.idveiculo = vlc.idveiculo
19 inner join
20 c_forma_pagamento fpg on ors.idforma_pagamento = fpg.idforma_pagamento

Nota: Na Figura 8 está sendo mostrado somente o cadastro da parte mestre do relacionamento mestre

detalhe, ou seja, a tabela . Mais à frente será mostrada a construção e configuração

completa do relacionamento entre essa tabela e a .

Na Listagem 15 encontra-se o código para a criação dinâmica do data module de cadastro ( )


entre as linhas 01 e 05, enquanto que entre as linhas 06 e 22 está a codificação para a abertura da janela. Pode-se notar
que ambos os eventos são idênticos aos anteriormente apresentados para as janelas básicas de cadastro.

12 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

Listagem 15. Codificação na janela de cadastro de ordens de serviço.

02 begin
03 inherited;
04 dtmOrdemServicoCadastro := TdtmOrdemServicoCadastro.Create(Self);
05 end;
06 procedure TfrmOrdemServicoCadastro.FormShow(Sender: TObject);
07 begin
08 inherited;
09 if FId = 0 then
10 begin
11 dtmOrdemServicoCadastro.qryOrdemServico.Close;
12 dtmOrdemServicoCadastro.qryOrdemServico.SQL.Add
(' where ors.idordem_servico is null');
13 dtmOrdemServicoCadastro.qryOrdemServico.Open;
14 dtmOrdemServicoCadastro.qryOrdemServico.Insert;
15 end else
16 begin
17 dtmOrdemServicoCadastro.qryOrdemServico.Close;
18 dtmOrdemServicoCadastro.qryOrdemServico.SQL.Add
(' where ors.idordem_servico = ' + QuotedStr(IntToStr(FId)));
19 dtmOrdemServicoCadastro.qryOrdemServico.Open;
20 dtmOrdemServicoCadastro.qryOrdemServico.Edit;
21 end;
22 end;

Por fim, na Listagem 16 é mostrado a codificação para definição da variável (linha 02) e os eventos
para alteração (linhas 05 até 18) e inclusão de novos registros (linhas 19 até 27). Quando este segundo evento for
chamado ao clique do botão alterar, o SQL da Listagem 14 é executado e os dados são carregados no formulário
conforme a Figura 8.

Listagem 16. Codificação na janela de listagem de ordens de serviços.

01 private
02 FJanelaCadastro: TfrmOrdemServicoCadastro;
03 public
04 end;
05 procedure TfrmOrdemServicoListagem.btnAlterarClick(Sender: TObject);
06 begin
07 inherited;
08 if not BindSourceDB1.DataSet.IsEmpty then
09 begin
10 FJanelaCadastro := TfrmOrdemServicoCadastro.Create(Self);
11 FJanelaCadastro.FId := BindSourceDB1.DataSet.FieldByName
('idordem_servico').AsInteger;
12 FJanelaCadastro.ShowModal;
13 FJanelaCadastro.DisposeOf;
14 BindSourceDB1.DataSet.Refresh;
15 end
16 else
17 ShowMessage('Nenhum registro está selecionado.');
18 end;
19 procedure TfrmOrdemServicoListagem.btnIncluirClick(Sender: TObject);
20 begin
21 inherited;
22 FJanelaCadastro := TfrmOrdemServicoCadastro.Create(Self);
23 FJanelaCadastro.FId := 0;
24 FJanelaCadastro.ShowModal;
25 FJanelaCadastro.DisposeOf;
26 BindSourceDB1.DataSet.Refresh;
27 end;

Nota: É importante frisar novamente que o evento para exclusão dos registros deve ser programado
individualmente em cada uma das janelas de listagem de registros, conforme mostrado na Listagem 7

13 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

usuário possa selecionar registros por meio dos botões de pesquisa que foram adicionados no cadastro de ordens de
serviços. Nesta janela ( ), existe um botão para escolher o cliente ( ), um
para o veículo ( ) e outro para a forma de pagamento ( ) ao lado de
cada um dos campos.

O objetivo desses botões é que quando clicados, seja exibida uma nova janela na qual o usuário seja capaz de fazer
uma pesquisa nessas tabelas e escolha o registro correspondente. Um componente muito utilizado para essa função é
o , porém, ele apresenta algumas desvantagens. Tomando como exemplo a seleção de clientes, a primeira
desvantagem é que todos os registros devem ser carregados neste componente para que o usuário possa selecioná-los.
Desta forma, é necessário executar uma consulta sem nenhum filtro do tipo na tabela de clientes, fato que
acarretará em lentidão no sistema. Em um sistema comercial podem existir muitos clientes cadastrados, portanto, o
tempo de execução desta consulta será de acordo com a quantidade de registros na base de dados. Outra desvantagem
é que no combobox padrão é possível listar somente um campo, ou seja, somente o nome do cliente (ou algum outro
campo) poderia vir a ser utilizado como filtro para seleção e escolha do registro desejado. Por fim, outra desvantagem
é que a visualização dos dados em um combobox não é tão clara quanto utilizar uma nova janela de pesquisa, visto que
é possível observar somente a lista dos campos com poucas possibilidades de adicionar campos adicionais que possam
vir a facilitar a escolha do cliente, por exemplo.

Nota: As desvantagens sobre o combobox apresentadas dizem respeito ao componente com suas
configurações padrões. Nas versões mais atuais do RAD Studio, existem maneiras mais personalizadas

para tratar alguns desses problemas apresentados; porém, na maioria dos casos a utilização de janelas de

pesquisa é uma melhor prática do que utilizar combobox. Além disso, existem componentes de terceiros

que implementam algumas melhorias no que dizem respeito ao desempenho do combobox.

Devido a essas desvantagens é que faremos uso de uma janela de pesquisa para cada um dos campos que possuem
chaves estrangeiras no cadastro das ordens de serviço. Para iniciar a construção dessas janelas, um novo formulário
deve ser criado no projeto (File>New>Multi-Device Form – Delphi) com o nome de . Assim como
para as outras funcionalidades anteriormente implementadas, essa janela será a tela base para a realização das
pesquisas, sendo que todas as janelas de pesquisa herdarão desta tela base utilizando os conceitos de herança da
orientação a objetos. A Figura 10 apresenta essa janela, a qual é composta por um ( ) configurado
com a propriedade = , um ( ), um ( ), um ( com o
configurado para para ocupar toda a janela), mais um com o alinhamento = , e por fim,
mais dois ( e ). Adicionalmente, encontra-se um ( ) que fará a
consulta nas tabelas correspondentes. Além disso, é necessário que essa janela tenha acesso ao data module de
conexão ( ), por meio do comando . Por fim, dois últimos ajustes é configurar a propriedade
do botão selecionar para e do botão cancelar para . A duas são responsáveis por fechar a
janela de pesquisa (o fluxo do programa volta para a janela que a chamou somente depois que a mesma for fechada,
travando o sistema nesta janela), porém a primeira será utilizada quando o usuário selecionar um registro e a segundo
quando o usuário não selecionar um registro e deseja cancelar a operação. Em resumo, quando o usuário abrir essa
janela será possível informar os parâmetros de pesquisa no , e logo após, clicando no botão pesquisar os
registros serão exibidos na grade de dados no centro da tela. E por fim, clicando em selecionar tanto o identificador do

14 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

registro (chave primária) quanto o nome serão copiados para a janela que a chamou, ou seja, o cadastro de ordens de
serviços.

Nota: Vimos no início desse curso que todos os componentes de acesso a dados foram adicionados em
data modules separados, ou seja, um novo arquivo para cada tipo de funcionalidade. Como essa janela de
pesquisa é bastante simples e não tem uma tendência de aumentar sua complexidade. Contudo, vamos

optar agora por adicionar o ( ) na própria janela.

A Listagem 17 apresenta o código que deve ser definido no evento do botão pesquisar ( ), o qual
fecha a consulta (linha 03), passa um parâmetro do tipo de acordo com o que for digitado na caixa de texto
(linha 04) e por fim, abre a consulta para retornar os resultados (linha 05). Esse parâmetro será definido
individualmente em cada uma das janelas de pesquisa de acordo com a tabela correspondente, contudo, somente
aceitará caracteres. Observe também que o conteúdo do campo de texto ( ) é concatenado com os
caracteres “%” (porcentagem) tanto no início quanto no final. Isso é necessário para que sejam retornados os registros
que contenham os caracteres informados pelo usuário, independentemente da posição em que tais caracteres
encontram-se no respectivo campo. Por meio da definição deste código, é possível observar a grande vantagem em
utilizar herança visual e de código, pois esse mesmo código funcionará de igual maneira para todas as janelas de
pesquisa que serão criadas posteriormente.

Listagem 17. Implementação do botão de pesquisa da janela de pesquisa padrão.

01 procedure TfrmPesquisaPadrao.btnPesquisarClick(Sender: TObject);


02 begin
03 qryDados.Close;
04 qryDados.Params[0].AsString := '%' + edtPesquisa.Text + '%';
05 qryDados.Open();
06 end;

Após a janela padrão estar concluída, o próximo passo é criar a janela para pesquisa de clientes que utilizará os
conceitos de herança. Para isso, é necessário acessar a opção de menu e escolher
, atribuindo o nome para a nova janela criada.

Nota: Como estamos trabalhando com a criação dinâmica dos formulários em tempo de execução via
código, é importante salientar que todos os formulários criados devem ser retirados da opção para

criação automática das janelas quando o sistema é carregado. Isso é conseguido acessando
e arrastando-os para .

Como o objetivo é que o usuário possa pesquisar os clientes de acordo com o que for informado no campo de texto
( ), é necessário informar o seguinte comando SQL nesta propriedade da query ( ):

15 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

select idcliente, nome, cpf from c_cliente where nome like :nome

seja otimizado para evitar tráfegos na rede de campos desnecessários. Neste exemplo, estamos considerando como
principais somente o nome e o CPF do cliente, porém, esses campos variam de contexto para contexto e uma análise
sobre quais dados são mais importantes deve ser realizada juntamente com o usuário do sistema. Note que existe um
parâmetro que é indicado pela presença do caractere “:” (dois pontos), o qual receberá o valor digitado no campo
de texto conforme apresentado no código da Listagem 17. Na Figura 11 é apresentada esta janela, na qual pode-se
observar na porção superior que os campos foram adicionados no (botão direito na query>
), bem como foi realizado o Live Bindings entre a query e a grade de dados, de acordo com a
parte inferior da Figura 11 (os componentes e são adicionados automaticamente
quando o Live Bindings é efetuado).

Nota: Quando um parâmetro é definido diretamente pela propriedade utilizando o caractere “:”

(dois pontos), em geral o seu tipo de entrada e tipo de dados é configurado automaticamente. Porém,

caso ocorra algum erro é necessário acessar a propriedade da query e configurar a propriedade

para e a propriedade para .

Essas são todas as configurações visuais necessárias para criar uma nova pesquisa de clientes utilizando herança,
faltando agora realizar a chamada na própria janela de ordens de serviços ( ). Para isso, na
Listagem 18 é apresentado o código que deve ser implementado no evento do botão
desta janela. Na linha 03 é definido um objeto que possui o mesmo tipo da janela de pesquisa (é necessário acessar a
opção para ter acesso a ela) enquanto que na linha 06 esse objeto é criado para conseguirmos acessar
as funcionalidades. O comando condicional da linha 08 verifica se o usuário clicou o botão “selecionar” na janela de
pesquisa por meio do valor , que foi definido anteriormente na janela de cadastro padrão. Caso essa condição
seja satisfeita, é executado o código das linhas 10 e 11 para transferir para a janela de cadastro os respectivos campos
que foram selecionados pelo usuário na janela de pesquisa. Por exemplo, na linha 10 o campo (chave
estrangeira) da tabela de ordens de serviços recebe exatamente o selecionado pelo usuário; enquanto que
na linha 11 ocorre processo muito similar, com a diferença que o campo que foi retornado por meio de um
comando recebe o nome do cliente. Esse processo é necessário devido ao fato de que a chave primária da tabela
de clientes ( ) deve ser atribuída à chave estrangeira da tabela de ordens de serviço (também ) para
efetivar o relacionamento na base de dados. O campo que armazena o nome do cliente não existe na ordem de serviço,
e ele é copiado também para mostrar seu valor em tela para que o usuário possa saber qual efetivamente foi o cliente
selecionado. Observe que é também utilizado uma estrutura para garantir que o formulário seja
eliminado da memória na linha 14 quando a transferência dos valores for realizada.

Listagem 18. Abertura da pesquisa de clientes no cadastro de ordens de serviço.

01 procedure TfrmOrdemServicoCadastro.btnSelecionaClienteClick(Sender: TObject);


02 var

16 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

05 inherited;
06 APesquisaCliente := TfrmPesquisaCliente.Create(Self);
07 try
08 if APesquisaCliente ShowModal = mrOk then

10 dtmOrdemServicoCadastro.qryOrdemServicoidcliente.AsInteger :=
APesquisaCliente.qryDadosidcliente.AsInteger;
11 dtmOrdemServicoCadastro.qryOrdemServicocliente.AsString :=
APesquisaCliente.qryDadosnome.AsString;
12 end;
13 finally
14 APesquisaCliente.DisposeOf;
15 end;
16 end;

Nota: Além do campo , o SQL presente no data module que se relaciona com a janela de

cadastro das ordens de serviço também busca a placa do veículo e o nome da forma de pagamento. Esses
três campos foram obtidos por meio de e não são gravados na base de dados, sendo muito úteis

para a apresentação dos dados que estão vinculados com as chaves estrangeiras. Por padrão, esses
campos possuem a propriedade configurada para , ou seja, não podem ser modificados.

Porém, para que a linha 11 da Listagem 18 possa ser executada deve-se acessar a janela

da query (botão direito na query>Fields Editor), selecionar esses campos e marcar a propriedade
para .

A partir deste momento o sistema pode ser executado e o cliente das ordens de serviço existentes já pode ser alterado,
conforme apresentado na Figura 12. Note que essa figura apresenta o fluxo completo da janela, na qual
primeiramente são pesquisadas as ordens de serviço ( – esquerda da figura), depois é clicado
no botão alterar para ser exibida a janela de cadastro ( – centro da figura) e por último,
quando o botão ao lado do nome do cliente é clicado é exibida a janela para seleção do novo cliente
( – direita da figura). Pode-se notar que foi digitado o conjunto de caracteres “jo” e são retornados
somente os clientes que o nome se enquadra nesses caracteres. Caso o usuário clique no botão selecionar, o nome
“Jones” entrará no lugar do nome “Adaiane” juntamente com o campo que efetivamente realiza o
relacionamento entre as tabelas. É importante salientar que com as funcionalidades implementadas até este momento
é possível somente alterar as ordens de serviço, visto que a codificação que as outras janelas de pesquisa ainda não
estão concluídas.

A seção anterior mostrou em detalhes o desenvolvimento da janela de pesquisa de clientes, que envolveu basicamente
as seguintes etapas: (i) criação da janela de pesquisa, (ii) configuração do comando SQL e Live Bindings e (iii)
codificação da janela de ordens de serviço. Com relação às outras janelas de pesquisa para o registro de uma ordem de
serviço, ainda falta desenvolver a pesquisa de veículos e de formas de pagamento. Com isso, o objetivo desta seção é
mostrar resumidamente as respectivas janelas juntamente com a codificação necessária para cada um. A Figura 13
apresenta a estrutura das janelas levando em consideração a pesquisa dos veículos, na qual é possível observar à

17 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

em consideração a placa do veículo (neste exemplo retornando todos os registros, não informando nenhum valor na
caixa de texto).

Nesta nova janela de pesquisa que deve ser construída seguindo os passos apresentados anteriormente, a propriedade
do componente deve possui a seguinte instrução:

select idveiculo, placa, modelo from c_veiculo where placa like :placa

Conforme pode ser observado, estão sendo retornados três campos e o filtro de consulta será a placa do veículo, de
acordo com o parâmetro ( ) definido no comando . A Listagem 19 apresenta o código que deve ser
informado no evento do botão da janela de ordens de serviços. Esse código é muito
similar ao da Listagem 18, com a diferença de que será aberta a pesquisa de veículos e de que na linha 10 está sendo
manipulada a chave estrangeira e na linha 11 o campo que é a junção ( ) no comando SQL
para recuperação das ordens de serviço. É importante enfatizar mais uma vez que nesta linha o campo está
na query das ordens de serviço ( ) e que a está sendo retornada da query de pesquisa
dos veículos ( ).

Listagem 19. Abertura da pesquisa de veículos no cadastro de ordens de serviço.

01 procedure TfrmOrdemServicoCadastro.btnSelecionaVeiculoClick(Sender: TObject);


02 var
03 APesquisaVeiculo: TfrmPesquisaVeiculo;
04 begin
05 inherited;
06 APesquisaVeiculo := TfrmPesquisaVeiculo.Create(Self);
07 try
08 if APesquisaVeiculo.ShowModal = mrOk then
09 begin
10 dtmOrdemServicoCadastro.qryOrdemServicoidveiculo.AsInteger :=
APesquisaVeiculo.qryDadosidveiculo.AsInteger;
11 dtmOrdemServicoCadastro.qryOrdemServicoveiculo.AsString := ]
APesquisaVeiculo.qryDadosplaca.AsString;
12 end;
13 finally
14 APesquisaVeiculo.DisposeOf;
15 end;
16 end;

Similarmente, a Figura 14 apresenta a estrutura das janelas levando em consideração a pesquisa das formas de
pagamento, na qual é possível observar à esquerda uma ordem de serviço selecionada e à direita a respectiva janela
com uma pesquisa sendo realizada levando em consideração o nome da forma de pagamento.

Nesta nova janela de pesquisa que deve ser construída seguindo os passos apresentados anteriormente, a propriedade

18 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

select idforma_pagamento, nome from c_forma_pagamento where nome like :nome

pagamento, de acordo com o parâmetro ( ) definido no comando . A Listagem 20 apresenta o código que
deve ser informado no evento do botão da janela de ordens de serviços. Esse
código é similar aos anteriores, com a diferença de que será aberta a pesquisa de formas de pagamento e de que na
linha 10 está sendo manipulada a chave estrangeira e na linha 11 o campo que é a
junção ( ) no comando SQL para recuperação das ordens de serviço. Assim como na pesquisa anterior, é
importante enfatizar novamente que nesta linha o campo está na query das ordens de serviço
( ) e que o está sendo retornado da query de pesquisa das formas de pagamento
( ).

Listagem 20. Abertura da pesquisa de formas de pagamento no cadastro de ordens de serviço.

01 procedure TfrmOrdemServicoCadastro.btnSelecionaFormaPagamentoClick(Sender: TObject);


02 var
03 APesquisaFormaPagamento: TfrmPesquisaFormaPagamento;
04 begin
05 inherited;
06 APesquisaFormaPagamento := TfrmPesquisaFormaPagamento.Create(Self);
07 try
08 if APesquisaFormaPagamento.ShowModal = mrOk then
09 begin
10 dtmOrdemServicoCadastro.qryOrdemServicoidforma_pagamento.AsInteger :=
APesquisaFormaPagamento.qryDadosidforma_pagamento.AsInteger;
11 dtmOrdemServicoCadastro.qryOrdemServicoforma_pagamento.AsString :=
APesquisaFormaPagamento.qryDadosnome.AsString;
12 end;
13 finally
14 APesquisaFormaPagamento.DisposeOf;
15 end;
16 end;

Por meio dessas implementações, além de realizar alterações nas ordens de serviços já existentes; é possível também
incluir novas ordens visto que todos os campos podem ser preenchidos, tanto os atributos caracteres, de data e
numéricos quanto as chaves estrangeiras.

Nota: O sistema completo desenvolvido nesta série de artigos tem o intuito de ser didático para que o
leitor tenha uma visão geral de todos os passos para a construção de um so3ware utilizando FireDAC e

FireMonkey. Por este motivo, algumas funcionalidades mais básicas e necessárias em um sistema
comercial não estão sendo mostradas, como por exemplo: não deixar o usuário selecionar um registro
caso não tenha selecionado nenhum na janela de pesquisa, não permitir que informações sejam

digitadas nos campos com no cadastro de ordens de serviço e também executar validação nos

campos antes de gravar uma nova ordem de serviço.

Nota: Conforme implementado inicialmente nesta série de artigos, está sendo utilizado o comando
após uma inserção ou alteração para que a grade de dados seja atualizada. Caso esse comando

não funcione adequadamente, é necessário utilizar os métodos e para que os dados sejam

recarregados.

19 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

O próximo passo será incluir alguns novos elementos na janela de ordens de serviço de modo que seja possível
visualizar todos os serviços vinculados à cada uma das ordens, caracterizando assim o relacionamento N-N mestre

module das ordens de serviços ( ) com o nome . Este novo


componente será responsável por carregar todos os serviços de uma ordem específica que será recebida como
parâmetro, sendo que para isso sua propriedade deve apontar para e a instrução SQL
mostrada na Listagem 21 deve ser preenchida na propriedade correspondente. Esse comando simplesmente retorna a
chave primária da tabela ( ), o nome do serviço e a quantidade executada, sendo que para isso
é feita junção da tabela com a tabela utilizando o comando nas
linhas 07 e 08. Na linha 09 é utilizado o comando para filtrar somente os registros que estão relacionados com a
ordem em questão, sendo utilizado para isso um parâmetro na linha 10 ( ). Assim como feito para as
outras listagens, foram buscados somente três campos para evitar sobrecarga no servidor de banco de dados e
diminuir tráfego na rede.

Listagem 21. SQL para retornar os serviços de uma ordem de serviço.

01 select
02 oss.idordem_servico_servico,
03 srv.nome as servico,
04 oss.quantidade
05 from
06 m_ordem_servico_servico oss
07 left outer join
08 c_servico srv on oss.idservico = srv.idservico
09 where
10 oss.idordem_servico = :idordem_servico

Após a definição do SQL, deve-se adicionar todos os campos no da query (botão direito na query>Fields
Editor>Add all fields) para posterior vinculação via Live Binding. Alterações também devem ser realizadas no
formulário ( ) com a adição de um ( ) e três botões inferiores
( , e ), conforme mostrado na Figura 15. O último passo no
que diz respeito às configurações visuais é fazer a vinculação desta nova grade com a query recém adicionada no data
module, assim como apresentado na parte direita superior da Figura 15.

Para que os serviços sejam carregados quando uma ordem já existente for aberta, é necessário acrescentar uma
codificação no evento do formulário, conforme mostrado na Listagem 22. Todo esse código foi implementado
anteriormente e é responsável pela abertura da janela, gerenciando quando trata-se de uma inclusão (linhas 04 até 09)
e quando é uma alteração (a partir da linha 10). A nova implementação está em negrito e encontra-se nas linhas 16, 17
e 18, na qual é nova query é fechada, o parâmetro é passado somente da ordem de serviço que está aberta e a consulta
é aberta para que os dados sejam carregados.

Listagem 22. Carregando os serviços da ordem de serviço.

01 procedure TfrmOrdemServicoCadastro.FormShow(Sender: TObject);


02 begin

20 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

05 begin
06 dtmOrdemServicoCadastro.qryOrdemServico.Close;
07 dtmOrdemServicoCadastro.qryOrdemServico.SQL.Add
(' where ors.idordem_servico is null');

09 dtmOrdemServicoCadastro.qryOrdemServico.Insert;
10 end else
11 begin
12 dtmOrdemServicoCadastro.qryOrdemServico.Close;
13 dtmOrdemServicoCadastro.qryOrdemServico.SQL.Add
(' where ors.idordem_servico = ' + QuotedStr(IntToStr(FId)));
14 dtmOrdemServicoCadastro.qryOrdemServico.Open;
15 dtmOrdemServicoCadastro.qryOrdemServico.Edit;
16 dtmOrdemServicoCadastro.qryOrdemServicoServico.Close;
17 dtmOrdemServicoCadastro.qryOrdemServicoServico.Params[0].AsInteger := FId;
18 dtmOrdemServicoCadastro.qryOrdemServicoServico.Open;
19 end;
20 end;

Desta forma, toda vez que uma ordem é aberta os serviços correspondentes também são carregados. A inclusão e
edição de serviços será realizada de forma similar ao que foi apresentado para os outros cadastros, ou seja, quando os
respectivos botões forem acionados ( e ) será aberta uma nova janela que será
responsável por essas duas operações. Com relação à exclusão, a Listagem 23 apresenta o código que deve ser inserido
no evento do botão . Na linha 04 é mostrada para o usuário uma mensagem de
confirmação, e se a operação for confirmada na linha 06 é executado o comando e na linha 07 o comando
para atualização dos registros na grade. É importante salientar que para esta operação está sendo utilizado o
componente , o qual foi gerado automaticamente pelo RAD Studio quando o Live Bindings foi efetuado.

Listagem 23. Exclusão de serviços da ordem de serviço.

01 procedure TfrmOrdemServicoCadastro.btnExcluirServicoClick(Sender: TObject);


02 begin
03 inherited;
04 if Application.MessageBox('Confirma a exclusão do registro selecionado?',
'Confirmação', MB_YESNO + MB_ICONQUESTION) = IDYES then
05 begin
06 BindSourceDB2.DataSet.Delete;
07 BindSourceDB2.DataSet.Refresh;
08 end;
09 end;

Nota: É necessário realizar a importação dos seguintes para que o da

Listagem 23 seja reconhecido no projeto: e .

Conforme mencionado anteriormente, a estrutura para cadastrar um serviço para uma ordem de serviço seguirá a
mesma ideia dos demais cadastros. Para isso, primeiramente é necessário criar um novo data module
(File>New>Other>Delphi Files>Data Module) chamado , adicionar um
( ), ligar sua propriedade ao (requer o comando File>Use Unit) e
preencher a propriedade conforme apresentado na Listagem 24 (os campos devem ser adicionados no
assim como para as outras queries). Esse código é bastante similar ao apresentado na Listagem 21, com a
diferença de que neste momento precisamos de todos os campos, pois esta query será responsável pela inclusão e
edição dos registros. Anteriormente, havia a necessidade de exibir somente os campos principais pelo motivo de ser
somente uma consulta aos dados para exibir ao usuário.

Listagem 24. Comando SQL para inserção e edição dos serviços da ordem de serviço.

21 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

01 select
02 oss.idordem_servico_servico,
03 oss idordem_servico,

05 oss.quantidade,
06 oss.valor_unitario,
07 oss.valor_total,
08 oss.observacao,
09 srv.nome as servico
10 from
11 m_ordem_servico_servico oss
12 left outer join
13 c_servico srv on oss.idservico = srv.idservico

Um novo formulário deve ser criado baseado no cadastro padrão definido anteriormente, seguindo o comando
. Na Figura 16 é apresentado o visual da janela, na qual é
possível observar que foram adicionados quatro para armazenar os dados ( , ,
e ), um para as observações ( ) e um
( ). Na parte inferior desta figura são apresentadas as vinculações que devem ser realizadas por
meio de Live Bindings entre a query criada anteriormente e os respectivos campos da janela (necessário executar o
comando para ter acesso ao data module). Outra configuração importante é ligar a propriedade
das ações e do ao componente que é adicionado
automaticamente no formulário quando o Live Bindings é feito.

Na Listagem 25 foi definida uma nova variável pública chamada (linhas 01 até 03), que receberá
como parâmetro o identificador (chave primária) da ordem de serviço para que seja possível o relacionamento mestre
detalhes. No restante da listagem está o código que deve ser programado nos eventos (linhas 04 até 08) e
(a partir da linha 09), os quais são responsáveis pela criação do data module e pelo gerenciamento para
verificar se a operação atual trata-se de uma inserção (entre as linhas 12 e 18) ou de uma alteração (a partir da linha
19). Com exceção da linha 18 que atribui para a chave estrangeira ( ) o valor recebido da variável
, este código é similar ao que já foi implementado para os outros cadastros e maiores detalhes sobre
seu funcionamento serão suprimidos desta seção.

Listagem 25. Codificação na janela de cadastro de serviços da ordem de serviço.

01 public
02 FIdOrdemServico: Integer;
03 end;
04 procedure TfrmOrdemServicoServicoCadastro.FormCreate(Sender: TObject);
05 begin
06 inherited;
07 dtmOrdemServicoServico := TdtmOrdemServicoServico.Create(Self);
08 end;
09 procedure TfrmOrdemServicoServicoCadastro.FormShow(Sender: TObject);
10 begin
11 inherited;
12 if FId = 0 then
13 begin
14 dtmOrdemServicoServico.qryOrdemServicoServico.Close;
15 dtmOrdemServicoServico.qryOrdemServicoServico.SQL.Add
(' where idordem_servico_servico is null');

22 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

18 dtmOrdemServicoServico.qryOrdemServicoServicoidordem_servico.AsInteger :=
FIdOrdemServico;
19 end else
20 begin

22 dtmOrdemServicoServico.qryOrdemServicoServico.SQL.Add
(' where idordem_servico_servico = ' + QuotedStr(IntToStr(FId)));
23 dtmOrdemServicoServico.qryOrdemServicoServico.Open;
24 dtmOrdemServicoServico.qryOrdemServicoServico.Edit;
25 end;
26 end;

Como pode ser observado na Figura 16, para cadastrar um serviço é necessário utilizar as janelas de pesquisa assim
como foi feito para a seleção do cliente, do veículo e das formas de pagamento. Para isso, uma nova janela deve ser
criada herdando de e nomeada para , assim como foi feito anteriormente para
as outras telas. A configuração principal é inserir o seguinte comando SQL na respectiva propriedade no componente
, o qual recebe um parâmetro para que o usuário possa pesquisar pelo nome do serviço:

select idservico, nome


from c_servico where nome like :nome

Na Listagem 26 é mostrado o código que deve ser implementado no evento do botão para pesquisa de
serviços ( ), o qual carrega uma nova janela de pesquisa e faz a atribuição da chave estrangeira na
linha 10 e do campo que é retornado como na linha 11.

Listagem 26. Abertura da pesquisa de serviços no cadastro de serviços da ordem de serviço.

01 procedure TfrmOrdemServicoServicoCadastro.btnSelecionaServicoClick(Sender: TObject);


02 var
03 APesquisaServico: TfrmPesquisaServico;
04 begin
05 inherited;
06 APesquisaServico := TfrmPesquisaServico.Create(Self);
07 try
08 if APesquisaServico.ShowModal = mrOk then
09 begin
10 dtmOrdemServicoServico.qryOrdemServicoServicoidservico.AsInteger :=
APesquisaServico.qryDadosidservico.AsInteger;
11 dtmOrdemServicoServico.qryOrdemServicoServicoservico.AsString :=
APesquisaServico.qryDadosnome.AsString;
12 end;
13 finally
14 APesquisaServico.DisposeOf;
15 end;
16 end;

A última etapa para que a janela de cadastro dos registros detalhe funcione para inclusões e alterações é programar os
eventos dos botões incluir e alterar da Figura 15 ( e ) da janela de
cadastro de ordens de serviços ( ). Na Listagem 27 é apresentado entre as linhas 01 e 10 o
código para inclusão, enquanto que entre as linhas 11 e 24 encontra-se o para alteração. Esse código é muito similar ao
que foi utilizado para a abertura das janelas dos outros cadastros e maiores detalhes serão suprimidos, com exceção da
linha 06 que acessa a variável da janela e a preenche com o
identificar da ordem de serviço que está aberta no momento. Esse código que efetivamente realiza a ligação mestre
detalhe entre as duas tabelas.

Listagem 27. Abertura da pesquisa de serviços no cadastro de serviços da ordem de serviço.

23 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

03 inherited;
04 FJanelaCadastro := TfrmOrdemServicoServicoCadastro.Create(Self);
05 FJanelaCadastro.FId := 0;
06 FJanelaCadastro FIdOrdemServico :=

07 FJanelaCadastro.ShowModal;
08 FJanelaCadastro.DisposeOf;
09 BindSourceDB2.DataSet.Refresh;
10 end;
11 procedure TfrmOrdemServicoCadastro.btnAlterarServicoClick(Sender: TObject);
12 begin
13 inherited;
14 if not BindSourceDB2.DataSet.IsEmpty then
15 begin
16 FJanelaCadastro := TfrmOrdemServicoServicoCadastro.Create(Self);
17 FJanelaCadastro.FId := BindSourceDB2.DataSet.FieldByName
('idordem_servico_servico').AsInteger;
18 FJanelaCadastro.ShowModal;
19 FJanelaCadastro.DisposeOf;
20 BindSourceDB2.DataSet.Refresh;
21 end
22 else
23 ShowMessage('Nenhum registro está selecionado.');
24 end;

A Figura 17 apresenta o processo completo do cadastro (da esquerda para a direita), na qual é possível observar a
listagem das ordens de serviço, o cadastro das ordens e a inclusão de um novo serviço juntamente com a tela de
pesquisa. Desta forma, já é permitido cadastrar uma ordem de serviço completa utilizando as janelas de pesquisa para
preenchimento das chaves estrangeiras bem como dos serviços vinculados às ordens.

Nota: Neste artigo foi utilizada uma abordagem manual para fazer a ligação mestre detalhe entre as

tabelas, porém, o FireDAC possui recursos próprios para o gerenciamento deste tipo de configuração.

Consulte a seção Links para mais informações.

A última implementação que faremos será o cálculo dos valores totais, tanto para a tabela de detalhe quanto para a
tabela mestre. Na tabela detalhe ( ) estão disponíveis os campos quantidade, valor unitário e
valor total; sendo que atualmente o próprio usuário que deve preencher o valor total. Como este campo é diretamente
relacionado aos outros dois, uma melhor maneira é criar um procedimento que faça a alteração automática do valor
total toda vez que uma alteração for realizada no valor unitário ou na quantidade. Na Listagem 28 é apresentada a
codificação que deve ser implementada no arquivo , na qual na linha 02 é definido a assinatura
do procedimento na seção do formulário. Apertando o conjunto de teclas CTRL +
SHIFT + C será gerada a implementação, conforme mostrado entre as linhas 03 e 06. Conforme pode ser observado,
esse código somente multiplica a quantidade pelo valor unitário e atribui para o campo valor total. Para que esse
procedimento seja executado, é necessário programar o evento tanto do campo quantidade quanto do valor
unitário (o evento é acessado pelo ), de acordo com as linhas 07 a 10 (quantidade) e linhas 11 até 14 (valor
unitário). Nas linhas 09 e 13 o novo procedimento é invocado para realizar a multiplicação dos valores, ou seja, toda
vez que um desses campos for alterado no formulário de cadastro o valor total será automaticamente atualizado.

24 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

Listagem 28. Cálculo do valor total dos serviços.

02 procedure CalculaQuantidadeUnitario;
03 procedure TdtmOrdemServicoServico.CalculaQuantidadeUnitario;
04 begin
05 qryOrdemServicoServicovalor_total.AsFloat :=
qryOrdemServicoServicoquantidade.AsInteger *
qryOrdemServicoServicovalor_unitario.AsFloat;
06 end;
07 procedure TdtmOrdemServicoServico.qryOrdemServicoServicoquantidadeChange(Sender: TField);
08 begin
09 CalculaQuantidadeUnitario;
10 end;
11 procedure TdtmOrdemServicoServico.qryOrdemServicoServicovalor_unitarioChange
(Sender: TField);
12 begin
13 CalculaQuantidadeUnitario;
14 end;

Com isso nossa aplicação está concluída e com as janelas de cadastro simples e mestre detalhe com as operações de
inclusão, alteração e exclusão funcionando. Conforme pôde ser percebido nas implementações, muitas partes do
código fonte podem ser otimizadas de modo a passar alguns parâmetros e as janelas já se comportarem da mesma
forma que desenvolvemos, mas sem a necessidade de repetição de código. Isso foi visto na janela padrão de pesquisa,
em que um mesmo código fonte (passagem do parâmetro e abertura da query) é utilizado para todas as janelas. Outras
melhorias podem incluir: validação de campos, verificações se algum registro está selecionado na tela de pesquisa,
atualização ( ) das grades de dados e até mesmo uma função para calcular o valor total de todos os serviços de
uma ordem de forma automática, de modo que não seja necessário que o usuário digite o valor total na tabela mestre.

Neste exemplo você terá acesso a uma base de dados completa e atualizada com todos os CEPs do Brasil com código do IBGE no formato SQL. Faça download e a incorpore

em seus projetos.

Neste DevCast separamos para você quatro dicas rápidas e super úteis sobre bancos de dados com as quais você poderá otimizar a modelagem e desempenho das suas

aplicações.

Aprenda como modelar o relacionamento entre produtos e categorias em uma base de dados relacional. Veja como modelar essa relação como 1:N ou N:N e quais as

implicações de cada escolha.

Aqui você encontra o Guia de estudo ideal para aprimorar seus conhecimentos nos principais Banco de Dados do mercado. Escolha o seu e bons estudos!

Neste guia você encontrará os principais conteúdos que você precisa estudar, como desenvolvedor, para trabalhar com bancos de dados.

Essa guia terá como objetivo apresentar a modelagem de dados, desde seus primeiros passos com banco pequenos até a modelagem para bancos Big Data.

25 of 26 22/07/2020 14:32
FireMonkey e FireDAC: Construindo uma aplicação completa - Parte II https://www.devmedia.com.br/firemonkey-e-firedac-construindo-uma-ap...

26 of 26 22/07/2020 14:32