Você está na página 1de 52

ClubeDelphi

EDITORIAL
O AJAX representa a segunda geração dos aplicativos para Web, fo-
cados na colaboração entre pessoas, um dos pilares da Web 2.0. Não
falamos apenas de uma evolução na plataforma Web, mas de uma
Ano 7 - 87ª Edição - 2007 - ISSN 1517990-7 Impresso no Brasil mudança profunda em como os aplicativos são escritos. Tecnicamente
falando, o AJAX permite a comunicação assíncrona de páginas com
Corpo Editorial Atendimento ao Leitor o servidor Web, permitindo ainda a atualização parcial de pequenas
Diretor Editorial e Administrativo A DevMedia conta com um departa- porções da tela, ao invés do refresh total. Suas aplicações vão ser se-
Gladstone Matos mento exclusivo para o atendimento ao melhantes a aplicações Desktop e agora sim, cada vez mais veremos
gladstone@neoficio.com.br leitor. Se você tiver algum problema no
recebimento do seu exemplar ou pre- sistemas sendo escritos para Web e não mais para Desktop. Nesta
Editor de Arte cisar de algum esclarecimento sobre edição, o Fabrício traz um excelente artigo que mostra a integração
Vinicius O. Andrade assinaturas, exemplares anteriores, Delphi e AJAX, aliados na construção de uma aplicação de pesquisas
viniciusoandrade@gmail.com endereço de bancas de jornal, entre
outros, entre em contato com: incrementais em bancos de dados Firebird.
Capa Carmelita Mulin Nos artigos que produzi para esta edição, o tema principal é ASP.
Antonio Xavier www.devmedia.com.br/central/default.asp
webdesigner@devmedia.com.br (21) 2220-5375 NET. Aprenda a criar clientes em ASP.NET para um servidor DataSnap.
Kaline Dolabella Com isso, você poderá integrar seu atual sistema multicamadas com a
Editor Geral Gerente de Marketing e Atendimento
Guinther Pauli melhor tecnologia para desenvolvimento Web. No outro artigo, mostro
kalined@terra.com.br
guinther@devmedia.com.br (21) 2220-5375 como obter performance no acesso ao banco Firebird usando ADO.NET.

Editor Técnico Publicidade Saiba como funciona o Connection Pooling, como usar corretamente
Luciano Pimenta Para informações sobre veiculação de DataReaders e DataSets, cache, Stored Procedures e muito mais.
lucianopimenta@clubedelphi.net anúncio na revista ou no site entre em
O Adriano inicia nesta edição um mini-curso que mostra como criar
contato com:
Revisão um sistema completo de contas a pagar e cobrança, utilizando as
Kaline Dolabella
Rafael de Castro Santos publicidade@devmedia.com.br principais tecnologias atuais do desenvolvimento Delphi: arquitetura
rafael@devmedia.com.br
Para fechar parcerias ou ações espe- Client/Server, banco Firebird, interface VCL e acesso dbExpress. Em
Distribuição cíficas de marketing com a DevMedia, seu outro artigo, Adriano apresenta um artigo que há tempos era solici-
Fernando Chinaglia Dist. S/A entre em contato com:
Rua Teodoro da Silva, 907 tado: como imprimir em impressoras matriciais usando o Delphi. E para
Jeff Wendell
Grajaú - RJ - 206563-900 finalizar, o Maikel fala sobre segurança de sistemas.
jeff@devmedia.com.br
Sucesso a todos e boa leitura!

Guinther Pauli
Apoio guinther@devmedia.com.br
Microsoft Certified: MCP, MCAD, MCSD.NET
Borland Certified: Delphi 6, 7, 2005, 2006, Web, Kylix
A revista ClubeDelphi é parte integrante da assinatura ClubeDelphi
PLUS. Para mais informações sobre o pacote PLUS, acesse:
http://www.devmedia.com.br/clubedelphi/portal.asp Fale com o Editor
É muito importante para a equipe saber entre em contato com os editores, informan-
o que você está achando da revista: que do o título e mini-resumo do tema que você
tipo de artigo você gostaria de ler, que gostaria de publicar:
ÍNDICE artigo você mais gostou e qual artigo
você menos gostou. Fique a vontade para Guinther Pauli - Editor da Revista
entrar em contato com os editores e dar a guinther@devmedia.com.br
06 - AJAX – Buscas incrementais
sua sugestão!
Fabrício Desbessel Luciano Pimenta - Editor do Site
Se você estiver interessado em publicar
um artigo na revista ou no site ClubeDelphi, lucianopimenta@clubedelphi.net
12 - ASP.NET e DataSnap
Guinther Pauli
Portal do Assinante
18 - Performance no Firebird e ADO.NET A ClubeDelphi tem uma novidade para você que comprou este NÃO A
C
PER
Guinther Pauli exemplar na banca de jornal: você pode acessar GRATUITAMENTE,
o Portal do Assinante ClubeDelphi!

24 - Contas a Pagar e Cobrança Confira o que você encontra no Portal do Assinante:


- Mais de 400 Vídeo Aulas!
Adriano Santos
- 6 cursos online!
- 1 Livro Eletrônico sobre ADO.NET e BDP!
34 - Impressão em ECF - Mais de 140 Artigos Exclusivos!
Adriano Santos Para Utilizar o Portal do Assinante, acesse www.devmedia.com.br/clubedelphi/portal.asp
e utilize as informações abaixo: Login: DVM.CD e Senha: CLB87

44 - Segurança de Sistemas O acesso é válido por 30 dias a partida da data de lançamento da revista. Todos os
Maikel Marcelo Scheid meses a ClubeDelphi lhe dará uma senha válida para acessar o portal. Comprando a
revista regularmente em bancas, você terá acesso ininterrupto a ele!

Edição 87 - ClubeDelphi 3

Clube87.indb 3 26.07.07 10:28:54


Ask The Expert
Perguntas e Respostas
Dúvidas respondidas por Luciano Pimenta
(envie as suas para pimenta@devmedia.com.br)

Caixa de mensagem com tempo Listagem 1. Código evento OnClick do Button


Estou trabalhando em uma aplicação onde
uses ExtCtrls, ComCtrls;
não gostaria que o usuário demorasse a res- ...
ponder confirmações de caixas de mensagens procedure TForm1.Button1Click(Sender: TObject) ;
var
(com os botões Sim ou Não). Tem como eu AMsgDialog: TForm;
AProgressBar: TProgressBar;
colocar um timer ou algo que marcasse uma ATimer: TTimer;
begin
opção default? AMsgDialog := CreateMessageDialog(‘Rápido! Resposta Sim ou Não’, mtWarning, [mbYes, mbNo]);
Isabel Foster AProgressBar := TProgressBar.Create(AMsgDialog);
ATimer := TTimer.Create(AMsgDialog);
with AMsgDialog do

Primeiro criamos um diálogo utilizando try


Tag := 10; //segundos
CreateMessageDialog. Essa função retorna Caption := ‘Você tem 10 segundos’;
um objeto formulário com o diálogo, Height := 150;
with AProgressBar do
onde podemos adicionar uma barra de begin
Name := ‘Progress’;
progresso. Além disso, adicionamos um Parent := AMsgDialog;
Max := AMsgDialog.Tag; //segundos
Timer para atualizar a barra de progres- Step := 1;
so dinamicamente e exibimos o diálogo Top := 100;
Left := 8;
utilizando ShowModal. Implementamos o Width := AMsgDialog.ClientWidth - 16;
end;
evento OnTimer do Timer para verificar se o with ATimer do
begin
tempo limite foi atingido, caso verdadeiro, Interval := 1000;
fechamos o diálogo alterando a proprieda- OnTimer := DialogTimer;
end;
de ModalResult, pelo código, para mrCancel. case ShowModal of
ID_YES: ShowMessage(‘Respondeu “Sim”.’);
Caso contrário, utilizamos StepIt para atu- ID_NO: ShowMessage(‘Respondeu “Não”.’);
ID_CANCEL: ShowMessage(‘Tempo estourado!’)
alizar a barra de progresso. Adicione um end;
Button a um formulário e adicione o código
finally
da Listagem 1 no OnClick: ATimer.OnTimer := nil;
Free;
Adicione na seção private o DialogTimer end;
e implemente-o com o código mostrado end;
na Listagem 2.
Rode a aplicação e veja o resultado
Listagem 2. Código de implementação do DialogTimer
(Figura 1).
procedure TForm1.DialogTimer(Sender: TObject) ;
var
aPB: TProgressBar;
begin
if NOT (Sender is TTimer) then
Exit;
if ((Sender as TTimer).Owner) is TForm then
with ((Sender as TTimer).Owner) as TForm do
begin
aPB := TProgressBar(FindComponent(‘Progress’));
if aPB.Position >= aPB.Max then
ModalResult := mrCancel
else
aPB.StepIt;
end;
end;

Figura 1. Caixa de mensagem com ProgressBar

4 ClubeDelphi - Ask The Experts – Perguntas e Respostas

Clube87.indb 4 26.07.07 10:28:58


+400 vídeo aulas | 6 cursos online

www.clubedelphi.net/portal

Caro Leitor,
. : : DESTAQUE : : .
O portal ClubeDelphi PLUS é a continuação, na Web, da Confira no portal ClubeDelphi PLUS, 20 vídeo aulas sobre dicas avançadas
revista ClubeDelphi. O portal recebe um conteúdo novo de ClientDataSet
todo dia e hoje conta com: i) mais de 390 vídeo aulas; ii)
6 cursos online; iii) 1 livro eletrônico gratuito, de Guinther Curso Online de ClientDataSet
Pauli, sobre ADO.NET e BDP; iv) mais de 150 artigos http://www.devmedia.com.br/articles/listcomp.asp?keyword=cursoclientdataset
exclusivos (que não foram publicados na revista)!;
Acesse o portal ClubeDelphi PLUS e receba muito mais
conteúdo sobre Delphi! E o que é melhor: de graça!
Todo leitor da revista ClubeDelphi, seja ele assinante ou Últimas Vídeo-Aulas (Somente para assinantes)
comprador da revista em bancas, tem acesso ao portal (para
quem compra em bancas, o acesso é válido por 30 dias). Sistema completo com Delphi 7, dbExpress e Firebird 2.0
Se você é assinante, utilize o seu login e senha pessoais Acompanhe a criação de um sistema completo de gerenciamento de locadora,
para acessar o portal. Se você comprou em bancas, utilize usando Delphi, dbExpress e Firebird 2.0. Luciano Pimenta mostrará todos os passos
o login e senha publicados na página do editorial desta necessários para a criação do projeto em todas as fases de implementação, desde a
edição. criação do banco de dados, até a migração para .NET.
Confira a seguir as últimas novidades do portal!
Criando uma tela de consulta dinâmica – Partes IV e V
Boa leitura e sucesso!
Veja nessa vídeo aula de Paulo Quicoli, como criar um tela de consulta dinâmica.
Equipe DevMedia

Mini-curso de ClientDataSet - Partes VII a XVI


Veja nessas aulas de Guinther Pauli um mini-curso exclusivo sobre dicas avançadas
Curso Online - PLUS de ClientDataSet.

O portal ClubeDelphi PLUS conta com 6 cursos online, confira! Componente Window (Janela) no Delphi for PHP
Veja nessa vídeo aula de Fabricio Desbessel, como trabalhar com o componente
Sistema completo com Delphi 7, dbExpress e Firebird 2.0 Window no Delphi for PHP.
[Luciano Pimenta]
Apresentando a suíte UserControls - Parte II
Aplicações WEB com IW e Delphi 7 (Delphi Win32)!
Veja nessa vídeo aula de Paulo Quicoli a suíte User Controls.
[Guinther Pauli]

Aplicações client/server no Delphi 2006


Conhecendo o plug-in Castalia
[Luciano Pimenta]
Veja nessa vídeo aula de Adriano Santos, como utilizar o plugin castalia.
Criando uma Aplicação multi-camadas Completa com Delphi
[Guinther Pauli] Plug-in CanTools
Criando uma aplicação completa: sistema SysCash Veja nessa vídeo aula de Adriano Santos, como utilizar o plugin CanTools.
[Everson Volaco]

Aplicações Client/Server com dbExpress e Firebird


[Luciano Pimenta]

ClubeDelphi PLUS - ClubeDelphi 5

Clube87.indb 5 26.07.07 10:29:01


AJAX
Buscas incrementais

A
tecnologia AJAX continua sendo MagicAjax
a mais importante inovação em Esse framework é gratuito, está na release
aplicações Web. Temos vários 0.3.0 e pode ser baixado no endereço oficial
artigos e vídeo aulas que mostram a do projeto: www.magicajax.net. Para utilizar,
utilização dessa tecnologia em inúmeros precisamos adicionar uma referência para
ambientes e frameworks. Na edição 75 a DLL do MagicAjax no projeto ASP.NET.
apresentei o MagicAjax, um framework Crie um novo projeto do tipo ASP.NET
que agiliza em muito o desenvolvimento Web Application - Delphi for .NET. No Project
no Delphi 2006 de aplicações com a tec- Manager selecione o item References e clique
nologia AJAX. com o botão direito para abrir o menu de
Inúmeros foram os e-mails que recebi contexto e escolha Add Reference.
com questões de como e onde utilizar o Na tela de Add Reference, com a aba
AJAX. Um questionamento interessante .NET Assemblies selecionada, clique no
foi verificar a possibilidade de fazer uma Browse e encontre o arquivo MagicAjax.
busca incremental utilizando o AJAX, ao dll a partir do diretório onde você o des-
melhor estilo do Google, ou seja, você di- compactou. Clique em OK para fechar o
Fabrício Desbessel gitará as iniciais do texto e os resultados editor (Figura 1).
(fabricio@fabricio.pro.br) apresentados serão exibidos através de No Project Manager, clique duas vezes
é professor de Linguagem de Programação do uma consulta ao banco de dados. no arquivo Web.config pois precisaremos
Curso Técnico em Informática do Colégio Fre- Isso tudo sem refresh na página da editá-lo, informando à aplicação para tra-
derico Jorge Logemann de Horizontina/RS e
pesquisa. Criei um exemplo e mostro tar as requisições de forma diferenciada,
da FAHOR Faculdade Horizontina. Delphiano de
coração está sempre disposto a provar que com neste artigo como implementá-lo, passo através do MagicAjax.
o Delphi sempre teremos a melhor solução. Site a passo. Procure pela sessão <httpModules> e
www.fabricio.pro.br.

6 ClubeDelphi - AJAX - Buscas incrementais

Clube87.indb 6 26.07.07 10:29:10


AS P.NET

adicione o seguinte código:

<add name="MagicAjax"
type="MagicAjax.MagicAjaxModule, MagicAjax"/>

Também podemos adicionar os com-


ponentes à Tool Palette, através do menu
Component>Install .NET Components.
Clique no Select an Assembly, encontre a
DLL e clique em OK. Agora teremos dis-
ponível os componentes da Figura 2.

Pesquisa incremental
Vamos criar uma consulta de dados na
tabela Employee do banco Employee.fdb,
através do Firebird Data Provider (veja
box). Adicione um FbConnection, clique na
propriedade ConnectionString e configure
suas propriedades conforme a Figura 3.
Figura 1. Adicionando referência ao MagicAjax
Agora, adicione um FbCommand, confi-
gurando sua propriedade Connection para
o fbConnection1 e CommandText conforme
o código a seguir:

select EMP_NO, FIRST_NAME


from EMPLOYEE
where Upper(FIRST_NAME) like @NOME
order by FIRST_NAME

Abra o editor da propriedade Parameters


do FbCommand e adicione um novo parâ-
metro, configurando seu ParameterName
para “@NOME” e SourceColumn para
“FIRST_NAME”. Verifique se o FbDbType
está como VarChar.

Design da aplicação
Inicialmente devemos colocar o AjaxPanel
encontrado na paleta Ajax. Aumente seu ta-
manho, pois todos os outros componentes
serão dispostos dentro do mesmo. O Ajax-
Panel funciona como um painel de compo- Figura 2. Componentes instalados no Delphi
nentes e todas as ações dos componentes
que estão dentro serão realizada através de
chamadas assíncronas ao servidor, ou seja,
utilizando a tecnologia AJAX.
Dentro do AjaxPanel, digite o texto “Pri-
meiro Nome:” e, abaixo, insira um TextBox
da paleta Web Controls. Ao lado insira um
outro TextBox e reduza seu tamanho. Abai-
xo do TextBox1 acrescente um ListBox.
Uma pesquisa incremental tem seu
funcionamento a cada tecla que o usuário
digita, trazendo resultados referentes
às iniciais digitadas e possibilitando a
escolha de um dos resultados que com-
pletará o texto. Figura 3. Propriedades de conexão

Edição 87 - ClubeDelphi 7

Clube87.indb 7 26.07.07 10:29:25


Então, no TextBox1 colocaremos o código Como queríamos que a cada tecla a pes- uma variável de tipo StringBuilder com
da pesquisa ao banco de dados, no evento quisa fosse realizada teremos que utilizar a finalidade de criar o código JavaScript.
TextChanged, que corresponde a alteração alguns códigos JavaScript para controlar Verificamos se a consulta ao banco de
do texto, conforme Listagem 1. melhor a aplicação. No Page_Load da pá- dados retornou algum resultado, através
gina e coloque o seguinte código: da propriedade Items.Count do ListBox e
Funcionalidades da aplicação adicionamos um código JavaScript para
{ Fazer postback a cada digitação ou retorno }
Para o evento TextChanged ser disparado TextBox1.Attributes.Add('onkeyup', tornar visível ou não.
'__doPostBack(''TextBox1'','''')');
é necessário alterar a propriedade Auto- No final do código, verificamos se já
PostBack para True. Pode parecer estranho não existe registro do código JavaScript
habilitar o postback, mas fique tranqüilo, Utilizando o Attributes podemos adicio- na página e registramos, fazendo com
pois o MagicAjax interceptará e fará uma nar eventos e códigos JavaScript em um que esse código seja executado. Para
chamada assíncrona. Como essa é a única componente. O que fizemos no TextBox1 fazer o auto-completar que, ao selecio-
forma de fazer uma pesquisa incremental foi chamar o postback a cada vez que uma nar um item no ListBox com um clique
fica claro que sem AJAX nunca pensarí- tecla for pressionada. Teste novamente a de mouse, seja preenchido os campos,
amos em fazer esse tipo de pesquisa em aplicação e informe duas letras, por exem- coloque o código a seguir no evento Se-
uma aplicação Web, pois a cada alteração plo, “a” e “s” e veja que a pesquisa será lectedIndexChanged e altere a propriedade
de texto, teríamos que esperar a página mais próxima do resultado pretendido. AutoPostBack para True:
“ir e voltar”, levando um bom tempo. Também já é possível retornar com o backs-
TextBox1.Text := ListBox1.SelectedItem.Text;
Você já pode fazer o primeiro teste, po- pace e a pesquisa ser refeita (Figura 4). TextBox2.Text := ListBox1.SelectedValue;
rém você deverá digitar uma letra inicial, Agora vamos cuidar de ocultar o ListBox1
por exemplo, “a” e pressionar o TAB para e mostrar quando o resultado da pesquisa Ainda pensando na usabilidade, faremos
que aconteça o postback, ou seja, disparar tiver itens para exibir. Clique no componen- com que seja possível ao usuário digitar
o evento no servidor. te e mude sua propriedade Visible para False. as iniciais e se aparecerem itens no ListBox
Agora altere o código do TextChanged do ele possa navegar nesses itens com as setas
TextBox1 conforme mostra a Listagem 2. direcionais (para cima e para baixo).
Firebird Data Provider
No código da Listagem 2 declaramos Para tanto utilizaremos novamente
Para acessar o Firebird a partir de aplicações
ASP.NET, a melhor opção é usar o provider
Listagem 1. Código do TextChanged do TextBox1
do banco para o ADO.NET, chamado
Firebird Data Provider. Para instalá-lo, FbConnection1.Open;
try
acesse o endereço www.firebirdsql.com, FbCommand1.Parameters[‘@NOME’].Value:=TextBox1.Text.ToUpper +’%’;
clique no link Download e a seguir em ListBox1.DataSource := FbCommand1.ExecuteReader;
ListBox1.DataTextField := ‘FIRST_NAME’;
Firebird .NET Data Provider. Baixe e instale ListBox1.DataValueField := ‘EMP_NO’;
ListBox1.DataBind;
a última versão do Data Provider for .NET finally
Framework 1.1, bastando seguir os passos FbConnection1.Close;
end;
no assistente que será iniciado.
Neste artigo usaremos versão 1.7 do Listagem 2. TextChanged do TextBox1 exibindo/ocultando o ListBox
provider, versão mais recente disponível
uses System.Text;
até o fechamento desta edição. Após a ...
instalação, no Delphi 2005/2006 clique procedure TWebForm1.TextBox1_TextChanged(sender: System.Object; e: System.EventArgs);
var
no menu Component|Installed .NET Codigo: StringBuilder;
Components. Digite “Firebird Data begin
fbConnection1.Open;
Provider” na opção Category, clique em try
FbCommand1.Parameters[‘@NOME’].Value := TextBox1.Text.ToUpper+’%’;
Select an Assembly e escolha o arquivo ListBox1.DataSource := FbCommand1.ExecuteReader;
FirebirdSql.Data.Firebird.dll, localizado no ListBox1.DataTextField := ‘FIRST_NAME’;
ListBox1.DataValueField := ‘EMP_NO’;
diretório de instalação do provider, por ListBox1.DataBind;
padrão em C:\Arquivos de programas\ Codigo := StringBuilder.Create;
Codigo.Append(‘<script language=”JavaScript”>)’);
FirebirdNETProvider(versão). if ListBox1.Items.Count > 0 then
begin
Clique em OK e observe que os novos Codigo.Append(‘document.getElementById( ‘+
componentes para acesso ao Firebird estão ‘”ListBox1$ajaxdest”).style.visibility = ‘+‘”visible”’);
ListBox1.Visible := True;
disponíveis no IDE. Maiores informações end
sobre o funcionamento e utilização else
Codigo.Append(‘document.getElementById(‘+
do Firebird Data Provider podem ser ‘”ListBox1$ajaxdest”).style.visibility = ‘+‘”hidden”’);
Codigo.Append(‘</script>)’);
encontradas na edição 66 da ClubeDelphi, if not IsClientScriptBlockRegistered(‘mostra’) then
ou ainda no curso de Delphi e ASP.NET da RegisterClientScriptBlock(‘mostra’, Codigo.ToString);
finally
DevMedia (www.devmedia.com.br/curso/ FbConnection1.Close;
ecommerce2005). end;
end;

8 ClubeDelphi - AJAX - Buscas incrementais

Clube87.indb 8 26.07.07 10:29:32


AS P.NET

funções JavaScript para verificar se a tecla


pressionada for a seta para baixo (código
40) e mudaremos o foco para o ListBox1
selecionando a primeiro item.
No evento Page_Load da página substi-
tua o código que colocamos anteriormen-
te pelo código da Listagem 3.
No código anterior, acrescentamos
códigos JavaScript para dois eventos que
podem acontecer: KeyPress e KeyUp. O
KeyPress acontece antes que o KeyUp e
por isso nesse evento tornamos o ListBox
visível. No KeyUp verificamos a tecla
pressionada e se for a seta para baixo
mudamos o foco para o ListBox e selecio-
namos o primeiro item.
Para fazer com que, ao pressionar o
ENTER no ListBox, os campos sejam
completados com o item selecionado e
o ListBox seja escondido, no Page_Load
acrescente o código da Listagem 4.
Com isso, você já pode testar a aplicação
com todo seu funcionamento. A aplica-
ção deve ser representada conforme a
Figura 5.
Para deixar a aplicação mais transparen- Figura 4. Testando a aplicação

Edição 87 - ClubeDelphi 9

Clube87.indb 9 26.07.07 10:29:33


te, vamos retirar a mensagem de Loading
Listagem 3. Código do Page_Load alterado
que o MagicAjax mostra, enquanto envia
{ Verifica se a tecla pressionada for seta para baixo(40) Se for: Verifica se ListBox está
e retorna informações ao servidor. Para visível. Muda o foco para o ListBox. Seleciona o primeiro Item e se não for: Executa um
fazer isso, teremos que modificar os arqui- PostBack }

vos JavaScript do MagicAjax. Abra o arqui- TextBox1.Attributes.Add(‘onkeyup’,


‘if((event.keyCode==40) && ‘+
vo Web.config e abaixo de <configuration> ‘(document.getElementById(‘’ListBox1$ajaxdest’’).’+
‘style.visibility==’’visible’’)){document.’+
coloque a declaração da Listagem 5. ‘getElementById(‘’ListBox1’’).focus();’+
Crie uma pasta com o nome “Script” ‘document.getElementById(‘’ListBox1’’).’+
‘selectedIndex=0; } else { __doPostBack’+
no diretório de sua aplicação e copie os ‘(‘’TextBox1’’,’’’’);}’);
{ Torna o ListBox visível }
arquivos AjaxCallObject.js e WebParts.js TextBox1.Attributes.Add(‘onkeypress’,
‘document.getElementById’+
para essa pasta. Esses arquivos são encon- ‘(‘’ListBox1$ajaxdest’’).style.’+
trados dentro da pasta Core\Script, onde ‘visibility=’’visible’’’);

foi descompactado o MagicAjax.


Listagem 4. Sumindo o ListBox ao pressionar ENTER
O MagicAjax cria esses arquivos au-
...
tomaticamente quando a aplicação é { Verifica se a tecla pressionada for ENTER (13) Se for, torna o ListBox oculto }
executada. Estamos fazendo isso, pois ListBox1.Attributes.Add(‘onkeypress’,
‘if(event.keyCode==13){document.’+
queremos personalizar a mensagem ‘getElementById(‘’ListBox1$ajaxdest’’).’+
‘style.visibility=’’hidden’’}’);
exibida. Podemos alterar o texto, mudar
a cor ou simplesmente fazer com que a Listagem 5. Declaração de arquivos JavaScript do MagicAjax
mesma não seja exibida.
<configSections>
Neste exemplo, vamos fazer com que a <section name=”magicAjax” type=“MagicAjax.Configuration.MagicAjaxSectionHandler,MagicAjax”/>
</configSections>
mensagem não seja mais exibida. Abra <magicAjax scriptPath=”~/Script”>
o arquivo AjaxCallObject.js e comente as <pageStore/>
</magicAjax>
linhas do CreateWaitElement(); colocando
“//” (duas barras).
Você encontrará duas chamadas para
a função. Depois de comentar teste no-
vamente a aplicação para verificar que a
mensagem não será mais exibida.

Conclusão
Com a chegada da tecnologia AJAX não
poderemos mais utilizar a desculpa que
aplicações Web são diferentes de desktop.
Com certeza pode ser mais difícil para
implementarmos as facilidades para o
usuário.
Mas aí que vem a grande pergunta:
Você quer facilidade para você que é
desenvolvedor ou quer facilidade para
o usuário do seu sistema? Quanto mais
concorrência, mais devemos pensar em
nossos clientes.
Coloquem a mão na massa e não dei- Figura 5. Aplicação em funcionamento
xem de utilizar AJAX em suas aplicações
Web.

www.devmedia.com.br/clubedelphi/portal.asp

Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a uma


série de vídeo aulas de Fabricio Desbessel, sobre AJAX no Delphi.

www.devmedia.com.br/articles/listcomp.asp?txtsearch=
utiliza%E7%E3o+da+Tecnologia+Ajax

10 ClubeDelphi - AJAX - Buscas incrementais

Clube87.indb 10 26.07.07 10:29:43


Clube87.indb 11 26.07.07 10:29:53
ASP.NET e DataSnap
Criando clientes Web para servidores Midas

O
DataSnap tem sido amplamente de todos os recursos e códigos implemen-
utilizado para a criação de apli- tados no servidor de aplicação.
cações distribuídas. O Delphi O Delphi for .NET deu suporte a uma
oferece um mecanismo quase que im- poderosa tecnologia para o desenvolvi-
batível em produtividade nesse sentido. mento de aplicações Web: o ASP.NET. O
Podemos criar aplicações multicamadas ASP.NET é sem dúvida hoje a tecnologia
de uma forma RAD e simples, sem nos mais robusta para o desenvolvimento de
preocuparmos com detalhes internos à soluções Web, e muitos desenvolvedores
tecnologia, como protocolos de comu- Delphi começaram a utilizá-la para novas
nicação. soluções.
Muitos desenvolvedores gastaram ho- Mas como ficam as aplicações mul-
ras, dias, meses, construindo um servidor ticamadas existentes, onde todo o BD,
de aplicação robusto que concentre todo acesso a dados e regra de negócio já estão
o acesso a dados e regras de negócio em implementados? Não seria interessante
Guinther Pauli uma única camada, compartilhada por oferecer uma opção Web para a mesma
(guinther@devmedia.com.br)
todas as aplicações clientes. arquitetura, convivendo em harmonia
é autor de mais de 100 artigos publicados e 200
vídeo-aulas e do livro “Delphi – Programação Quando falamos em DataSnap, no en- com aplicações cliente desktop já exis-
para Banco de Dados e Web”. É Bacharel em Sis- tanto, estamos falando em VCL. Muitos tentes?
temas de Informação, Microsoft Certified: MCP, sem dúvida sentiram a necessidade Esse é o propósito deste artigo: mostrar
MCAD, MCSD.NET, Borland Certified: Delphi 6, de oferecer não só um cliente desktop como construir uma interface Web para
7, 2005, 2006, Web e Kylix. Editor Geral das Re-
parar o servidor de aplicação, mas uma um servidor DataSnap, usando a tecno-
vistas .net Magazine, ClubeDelphi, WebMobile
(.NET) e Mr.Bool. Palestrante em vários eventos interface Web, de forma que clientes pu- logia mais robusta para esse propósito, o
pelo Brasil, como TechDay SP,RJ,POA,BH, Web dessem acessar a solução multicamadas ASP.NET. Clientes desktop Win32 e Web
Days e todas as edições da Borland Conference. de qualquer local do planeta, usufruindo poderão conviver lado a lado e poderão

12 ClubeDelphi - ASP.NET e DataSnap

Clube87.indb 12 26.07.07 10:29:58


BOA IDÉIA

compartilhar a mesma camada de negó- tomers”) apontando para esse SQLData- especificamente ADO.NET e DataSnap /
cio e acesso a dados, ou seja, o mesmo Set. Configure o parâmetro do SQLData- ClientDataSet.
servidor de aplicação. Set para Input e ftString. Dê também um Vamos ao primeiro passo. Toda vez que
valor padrão de “%” para ele. um cliente instancia um servidor COM+,
Preparando o banco de dados Dê um Build no projeto. Agora vamos por padrão o objeto assume a identidade
Neste exemplo vou utilizar um banco de instalar o servidor no catálogo do COM+. do usuário interativo, ou seja, do usuário
dados no Firebird 2.0, mas sinta-se a von- Para isso, podemos usar o próprio IDE logado no Windows.
tade para utilizar o banco de dados de sua do Delphi, através do menu Run>Install Quando acessamos uma página Web,
preferência. Para facilitar, vamos criar COM+ Objects. o ASP.NET juntamente com o IIS (ser-
uma tabela com uma estrutura bastante Na caixa que aparece, marque o objeto vidor Web) utiliza um usuário padrão
simplificada dentro de um novo banco de DM e na outra caixa, na opção Install into para instanciar o aplicativo, chamado
dados, chamado “CUSTOMER.FDB”. a new Application digite “ServerCom- IUSR_NomeDaMaquina, a menos que a
Para criar o banco e a tabela “CUSTO- Plus”. Clique OK e OK. Pronto, nosso autenticação esteja configurada no apli-
MERS” você pode utilizar o IBExpert servidor está instalado e pronto para ser cativo para usar algo diferente.
(www.ibexpert.com). A estrutura da ta- acessado. O problema é que esse usuário, que será
bela é mostrada na Figura 1. O script automaticamente usado pela nossa apli-
de criação da tabela pode ser visto na Requisitos para acessar o COM+ a cação ASP.NET, não possui privilégios
Listagem 1. partir do ASP.NET para instanciar objetos COM. Nesse caso
Ao criar a tabela, veja que definimos Três etapas precisam ser feitas para temos várias soluções. Uma delas seria
um Sequence para incrementar automa- podermos acessar o servidor COM+ Da-
ticamente o campo chave, nesse caso taSnap a partir de aplicações ASP.NET.
www.devmedia.com.br/clubedelphi/portal.asp
CustomerId. Se estiver usando o modo de A primeira diz respeito à segurança do
designer do IBExpert para criar a tabela, aplicativo, a segunda é relativa à compa- Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a uma
basta marcar a opção AutoInc e marcar a tibilidade dos aplicativos ASP.NET com vídeo aula de Guinther Pauli que mostra como criar servidores COM+.

seguir os itens Create Generator/Sequence o COM+ e a terceira diz respeito a inte-


www.devmedia.com.br/articles/viewcomp.asp?comp=983
e Create Trigger. roperabilidade entre .NET e Win32, mais
Criada a tabela, adicione alguns regis-
tros nela para facilitar nossos testes quan-
do formos criar a aplicação no Delphi.

Criando o servidor DataSnap


No Delphi, clique em File>New>Other>
Multitier>Transactional DataModule. Na
caixa de diálogo que aparece, digite “DM”
para o nome da CoClass. Uma CoClass é Figura 1. Estrutura da tabela CUSTOMERS
na verdade uma classe que implementa
uma interface COM. Salve a unit com o Listagem 1. Código de criação da tabela CUSTOMERS
nome de “uDM.pas” e o projeto com o
CREATE SEQUENCE GEN_CUSTOMERS_ID;
nome de “ServerComPlus.dpr”, dentro
CREATE TABLE CUSTOMERS (
de um diretório de mesmo nome. CUSTOMERID INTEGER NOT NULL,
Usando componentes dbExpress, con- FIRSTNAME
COMPANY
VARCHAR(50) NOT NULL,
VARCHAR(50) NOT NULL
figure uma conexão para o banco de );

dados criado, através do editor de cone- ALTER TABLE CUSTOMERS ADD CONSTRAINT
PK_CUSTOMERS PRIMARY KEY (CUSTOMERID);
xões do SQLConnection. Lembre-se de
colocar localhost na frente do caminho CREATE TRIGGER CUSTOMERS_BI FOR CUSTOMERS
ACTIVE BEFORE INSERT POSITION 0
do banco para que seja usado TCP/IP e AS
BEGIN
aplicações Web possam acessar o banco IF (NEW.CUSTOMERID IS NULL) THEN
NEW.CUSTOMERID = GEN_ID(GEN_CUSTOMERS_ID,1);
(elas são multi-thread, conexões locais não END
funcionam). Configure o LoginPrompt
para False. Listagem 2. Código SQL do SQLDataSet

Coloque um SQLDataset (“sqlCusto- select


CUSTOMERID,
mers”) e aponte-o para a conexão. Na FIRSTNAME,
propriedade CommandText retorne os re- COMPANY
from
gistros da tabela CUSTOMERS conforme CUSTOMERS
where
o select da Listagem 2. FIRSTNAME like :FIRSTNAME
Coloque um DataSetProvider (“dspCus-

Edição 87 - ClubeDelphi 13

Clube87.indb 13 26.07.07 10:30:02


trocar a conta para acesso anônimo, o Administrativas. O que você vai ver é uma opção This User. A seguir, desmarque a
que não seria uma boa idéia. Outra seria interface que permite configurar várias opção mostrada na Figura 3.
configurar a identidade do aplicativo opções dos objetos registrados, como A segunda modificação é na aplicação
ASP.NET, usando o impersonate. pacotes, componentes etc. ASP.NET. Você deve modificar a página
A que escolhi e acredito ser a mais se- Localize nossa aplicação, que deve se que vai acessar o aplicativo COM+, aces-
gura e fácil de implementar foi dizer ao chamar ServerComPlus, e dê um clique sando o código-fonte do arquivo ASPX. A
COM+ para rodar o objeto servidor com de direita para abrir as propriedades. Na diretiva Page deve conter agora o atributo
uma conta de um usuário específico. Para aba Identity configure as opções como AspCompat:
fazer isso, acesse os Serviços de Componen- mostrado na Figura 2, indicando uma <%@ Page language="c#" Debug="true"
Codebehind="WebForm1.pas"
tes dentro do Painel de Controle>Ferramentas conta válida (pode ser o seu usuário) na AutoEventWireup="false"
Inherits="WebForm1.TWebForm1"
AspCompat="true" %>

Essa configuração permite que a página


ASP.NET utilize o mesmo modelo de thre-
ad utilizado pelo servidor COM+, nesse
caso por padrão é single thread apartment
(STA).
O terceiro passo, e mais complicado, é
prover a comunicação entre ASP.NET e
DataSnap. O grande problema é que um
aplicativo ASP.NET não vai reconhecer
os formatos de DataPacket enviados por
um servidor DataSnap.
Uma solução seria usar XML, mas isso
envolveria criar um servidor SOAP e
não COM+. Veremos como resolver esse
problema a seguir.

Nota: Antes de prosseguir, eu realmen-


te sugiro que você crie uma aplicação
cliente para teste em Win32, para ver se
está tudo funcionando no seu servidor
DataSnap COM+. Isso porque a maioria
das mensagens de erro que possam vir
a acontecer na aplicação ASP.NET, devi-
do a um problema no servidor, não des-
creverão a causa exata do problema.

Criando a aplicação cliente ASP.NET


Vamos partir para o último passo, que é a
criação da aplicação cliente. Usando Delphi
for .NET inicie uma nova aplicação ASP.
NET. Já na página principal, no arquivo
Figura 2. Configurando a conta do usuário COM+
ASPX, faça a alteração na diretiva AspCom-
pat como descrito anteriormente. Desenhe
um formulário como mostra a Figura 4.
Os nomes dados aos componentes são:
“btSelect” e “btApply” para os botões,
“tbFind” para o TextBox e “DG” para o
DataGrid. Para colocar o botão Edit no
Grid, basta selecionar o DG e no Object
Inspector acessar a opção Property Builder.
No editor, acesse Columns e em Button
Column adicione uma coluna do tipo Edit,
Figura 3. Desmarcando o Enforce Access Checks
Update e Cancel.

14 ClubeDelphi - ASP.NET e DataSnap

Clube87.indb 14 26.07.07 10:30:03


BOA IDÉIA

Agora precisamos obter os dados do ser- AdoDataSet.pas”. O seu código é mos- O método Open não faz muita coisa,
vidor DataSnap. Para isso, no Click do Select trado na Listagem 5. ele na verdade chama o método interno
digite o código da Listagem 3. O código do OpenCds e chama o Convert para conver-
GridDataBind é mostrado na Listagem 4. Nota: Para facilitar e reduzir o código, ter os dados de DataPacket para DataSet.
O método da Listagem 3 mostra um per- declarei alguns membros como atribu- O OpenCds instancia um MidasConnection
sonagem novo na história. Para obter os tos simples na classe, mas o ideal é que (que é um wrapper para um TDComCon-
dados do servidor DataSnap, utilizamos você crie-os na forma de propriedades. nection) para conectar ao servidor COM+
um DataSet do ADO.NET para colocar os e utiliza um ClientDataSet interno para
dados em memória (sessão). Porém, o for- Para acessar o servidor DataSnap, foi armazenar os dados obtidos.
mato de dados desse DataSet não é o mes- necessário importar alguns namespaces O método Convert faz a varredura no
mo que é devolvido por um IAppServer do da VCL for .NET, mostrados no início DataPacket obtido (ClientDataSet) e ar-
servidor DataSnap. O formato usado pelo da unit. Você precisa referenciar os mazena em um DataTable, que pode ser
ClientDataSet em aplicações multicamadas assemblys a partir do Project Manager, acessado diretamente pela coleção Tables,
se chama DataPacket, e não é compatível através da opção Add Reference. Os pois nossa classe descende de DataSet. O
com o DataSet do ADO.NET. assemblys necessários são Borland.Vcl- primeiro passo é configurar as colunas e a
Para resolver o problema, criei uma DbRtl.dll, Borland.VclDSnap.dll e Borland. seguir obter os registros. O ApplyUpdates
classe descendente de DataSet que é VclDSnapCon.dll. é explicado a seguir.
capaz de acessar um servidor DataSnap,
chamada apropriadamente de MidasDa- Listagem 3. Método para obter os dados
taSet. Eu adicionei nessa classe algumas
procedure TWebForm1.btSelect_Click(
propriedades básicas para comunicação sender: System.Object; e: System.EventArgs);
Midas, como você pode ver no código as var
ds: MidasDataSet;
propriedades RemoteServer e ProviderNa- begin
ds := MidasDataSet.Create();
me, como se fosse um ClientDataSet. ds.RemoteServer := ‘ServerCOMPlus.DM’;
ds.ProviderName := ‘dspCustomers’;
A classe se comunica como um servidor ds.Param := tbFInd.Text;
COM+ por meio da interface IAppServer ds.Open(true);
session[‘ds’] := ds;
e recebe os dados em DataPackets, trans- GridDataBind();
end;
formando-os em um DataTable do ADO.
NET que fica interno ao DataSet. Listagem 4. Método para preencher o DataGrid
Com isso, podemos fazer o DataBind procedure TWebForm1.GridDataBind;
para um DataGrid. Para facilitar, eu colo- begin
DG.DataSource := session[‘ds’] as DataSet;
quei a classe MidasDataSet em uma unit DG.DataBind();
end;
separada, explicada a seguir.

A classe MidasDataSet para


comunicação COM+/.NET
Para implementar a classe uma nova
unit deve ser criada, chamada “Midas

Figura 4. Web Form principal da aplicação Figura 5. Aplicação ASP.NET acessando servidor DataSnap

Edição 87 - ClubeDelphi 15

Clube87.indb 15 26.07.07 10:30:05


Listagem 5. Código da unit MidasAdoDataSet Nota: Se o leitor observar, o que fiz aqui
unit MidasAdoDataSet;
interface foi exatamente o inverso da técnica que
uses
Borland.Vcl.MConnect, Borland.Vcl.Db, Borland.Vcl.DBClient, Borland.Vcl.Variants, System.Data;
demonstrei na edição passada, onde na
type ocasião foi preciso transformar um Da-
MidasDataSet = class (DataSet)
private Cds: TClientDataSet; taSet do ADO.NET em um Data Packet.
procedure Convert();
procedure OpenCds();
public
RemoteServer: string;
Executando a aplicação e clicando no
ProviderName: string; botão, já podemos testar o sistema (Figu-
Param: string;
procedure Open(KeepCdsInSession: boolean); ra 5). Você pode fornecer um parâmetro
procedure ApplyUpdates();
end; para o like implementado no servidor,
MidasConnection = class(System.&Object)
public digitando um texto no TextBox. Para sim-
DCom: TDCOMConnection;
public
plificar o exemplo, a classe MidasDataSet
constructor Create(ServerName: string); considera que o DataSet no servidor pos-
end;
implementation sui nenhum ou um único parâmetro.
procedure MidasDataSet.ApplyUpdates;
var
row: DataRow; Nota: Se a aplicação não rodou, verifique
Key: TField;
i: Integer; atentamente todos os passos realiza-
begin
for row in Tables[0].Rows do dos e também o código. Nesse tipo de
if row.RowState = DataRowState.Modified then
begin solução, a mensagem de erro que você
Key := cds.Fields[0];
cds.Locate(Key.FieldName, VarArrayOf([row[key.FieldName].ToString()]), []);
obtém pode não indicar o erro exato. Ex-
cds.Edit(); perimente trocar simplesmente o nome
for i := 1 to cds.FieldCount - 1 do
cds.Fields[i].AsString := row[cds.Fields[i].FieldName].ToString(); do ProviderName no código e veja que
cds.Post();
end; receberá a mirabolante mensagem “Ca-
{ Testar aqui as opções de insert e delete }
cds.ApplyUpdates(0);
tastrophic failure” ou “Falha Catastrófica”.
end;
procedure MidasDataSet.Convert();
var
i: integer;
col: DataColumn; Edição e ApplyUpdates
dt: DataTable;
row: DataRow; Após os dados serem obtidos do servidor,
begin
cds.Open();
pela nossa classe MidasDataSet, eles são
dt := DataTable.Create(); guardados em sessão (memória) para pode-
Tables.Clear();
Tables.Add(dt); rem ser manipulados pelo DataGrid do ASP.
for i := 0 to cds.Fields.Count - 1 do
begin NET. Para isso, usamos o objeto Session.
col := DataColumn.Create; Se observar bem, quando colocado em
col.ColumnName := cds.Fields[i].FieldName;
dt.Columns.Add(col); memória, nosso MidasDataSet terá dois
end;
while not cds.eof do ResultSets: o ClientDataSet originalmente
begin
row := dt.NewRow; obtido a partir do DataPacket e os dados
for i := 0 to Cds.FieldCount - 1 do
begin
do DataTable, que são as informações
row[cds.Fields[i].FieldName] := cds.Fields[i].AsString; convertidas para ADO.NET.
end;
dt.Rows.Add(row); Guardamos o ClientDataSet original
cds.Next;
end; em cache juntamente com o MidasData-
AcceptChanges();
end;
Set por um único motivo: precisamos
procedure MidasDataSet.Open(KeepCdsInSession: boolean); dele no ApplyUpdates. Quando chama-
begin
OpenCds(); mos o ApplyUpdates do nosso Midas-
Convert();
if not KeepCdsInSession then cds.Free(); DataSet, todos os valores alterados no
end;
procedure MidasDataSet.OpenCds(); DataTable são repassados de volta ao
var
Con: MidasConnection;
ClientDataSet, e então voltamos a fazer
begin uma comunicação DataSnap com o
con := MidasConnection.Create(RemoteServer);
cds := TClientDataSet.Create(nil); servidor de aplicação.
cds.ProviderName := ProviderName;
Cds.RemoteServer := Con.DCom;
Cds.FetchParams(); Nota: Caso não vá alterar o DataSet,
if Param <> ‘’ then Cds.Params[0].AsString := Param;
Cds.Open(); usando-o apenas para consulta, passe
end;
constructor MidasConnection.Create(ServerName: string); False para o parâmetro KeepCdsInSes-
begin
inherited Create(); sion do Open. Criei esse parâmetro com
DCom := TDCOMConnection.Create(nil);
DCom.ServerName := ServerName;
o propósito de otimizar a solução caso
end; não vá precisar editar os dados.
end.

16 ClubeDelphi - ASP.NET e DataSnap

Clube87.indb 16 26.07.07 10:30:06


BOA IDÉIA

Para testar o Apply, para habilitar a Listagem 6. Eventos EditCommand e CancelCommand do DataGrid

edição na interface da aplicação, pri- procedure TWebForm1.DG_EditCommand(


source: System.Object;
meiramente crie o código da Listagem 6 e: System.Web.UI.WebControls.DataGridCommandEventArgs);
begin
para o EditCommand e CancelCommand do DG.EditItemIndex := e.Item.ItemIndex;
DataGrid. O código simplesmente coloca GridDataBind();
end;
o grid em edição baseado na célula que
procedure TWebForm1.DG_CancelCommand(
o usuário clicou (botão Edit). source: System.Object;
e: System.Web.UI.WebControls.DataGridCommandEventArgs);
begin
Nota: O DataGrid do ASP.NET, na sua ver- DG.EditItemIndex := -1;
GridDataBind();
são 1.1, não suporta a edição automática end;
como estamos acostumados a fazer com
Listagem 7. Evento UpdateCommand do DataGrid
um DBGrid na VCL. Então tudo deve ser
feito manualmente, como obter os da- procedure TWebForm1.DG_UpdateCommand(
source: System.Object;
dos editados e jogar no DataSet. e: System.Web.UI.WebControls.DataGridCommandEventArgs);
var
ds: DataSet;
Quando o usuário clica no Update após a row: DataRow;
begin
edição de um registro no Grid, precisamos ds := session[‘ds’] as DataSet;
row := ds.tables[0].rows[e.Item.ItemIndex];
capturar os valores digitados nas células e row[‘FirstName’] := (e.Item.Cells[2].Controls[0] as TextBox).Text;
row[‘Company’] := (e.Item.Cells[3].Controls[0] as TextBox).Text;
devolver para o respectivo registro no Data DG.EditItemIndex := -1;
Set (nesse caso o MidasDataSet). Isso é feito GridDataBind();
end;
no evento UpdateCommand do DataGrid (Lis-
tagem 7). O código do Apply simplesmente Listagem 8. ApplyUpdates
chama o ApplyUpdates do MidasDataSet que procedure TWebForm1.btApply_Click(
estava em sessão (Listagem 8). A Figura 6 sender: System.Object; e: System.EventArgs);
begin
mostra a edição em execução. (Session[‘ds’] as MidasDataSet).ApplyUpdates();
GridDataBind();
end;
Nota: Para não estender o exemplo, eu
omiti as operações de inserção e exclusão.
Mas você pode facilmente se basear no có-
digo do Update para criar essas operações.

Conclusão
O exemplo aqui demonstrado é simples,
mas ilustra o poder da combinação propos-
ta. Como possíveis melhorias, sugiro que o
leitor incremente o MidasDataSet para que
se pareça ainda mais com um ClientDataSet,
usando e abusando dos recursos de IApp-
Server. O primeiro passo está dado.
A solução aqui apresentada vai agora
permitir que você crie soluções Web ba-
seadas em ASP.NET reaproveitando todo
o código da sua camada intermediária, ao
Figura 6. Testando a edição
mesmo tempo que mantém a compatibi-
lidade com clientes desktop.
Dessa forma, poderá oferecer ambas Links
as soluções, por exemplo, permitindo o Treinamento On-Line em ASP.NET e Delphi 2005/2006 da ClubeDelphi
acesso Win32 a partir de uma intranet, e www.devmedia.com.br/curso/ecommerce2005
o acesso Web a partir da Internet.

Edição 87 - ClubeDelphi 17

Clube87.indb 17 26.07.07 10:30:07


Performance no Firebird e ADO.NET
DataReaders, DataSets, Connection Pooling, Cache e Stored Procedures

N
este artigo destaco algumas Palette, coloque um FbConnection no Web
dicas interessantes para otimi- Form. Selecione o componente e no Object
zar aplicações ASP.NET com Inspector acesse o editor da propriedade
Firebird. Veremos como a tecnologia ofe-
rece poderosos recursos para tornar suas Provider ADO.NET Para Firebird
aplicações Web robustas e escaláveis,
usando o mínimo de esforço possível. Para acessar o Firebird no ASP.NET, você
Conheceremos os poderosos recursos pode utilizar o provider ADO.NET do pró-
de cache de dados, uso efetivo de Stored prio Firebird. Para baixá-lo, utilize o seguin-
Procedures, Connection Pooling e outras te endereço: www.firebirdsql.org/index.
técnicas avançadas. php?op=files&id=netprovider. A versão
Você aprenderá como usar DataSets utilizada é para o .NET Framework 1.1. A
em memória para evitar consultas des- instalação é bastante simples, basta execu-
necessárias ao servidor SQL e otimizar tar o instalador.
Guinther Pauli assim o tráfego de dados. Você também Para instalar no Delphi 2006, acesse o menu
(guinther@devmedia.com.br) conhecerá um pouco sobre o interessante Component>Installed Components. No
é autor de mais de 100 artigos publicados e 200
recurso de Connection Pooling do ADO. editor, digite “Firebird” em Category, clique
vídeo-aulas e do livro “Delphi – Programação
para Banco de Dados e Web”. É Bacharel em Sis- NET. Para construir os exemplos, utili- no botão Select an Assembly e escolha o
temas de Informação, Microsoft Certified: MCP, zarei o Delphi 2006 e o Firebird 2.0 como arquivo FirebirdSql.Data.Firebird.dll, que
MCAD, MCSD.NET, Borland Certified: Delphi 6, banco de dados. por padrão encontra-se em: C:\Arquivos de
7, 2005, 2006, Web e Kylix. Editor Geral das Re- programas\FirebirdNETProvider1.7. Clique
vistas .net Magazine, ClubeDelphi, WebMobile em OK e veja na Tool Palette os componen-
(.NET) e Mr.Bool. Palestrante em vários eventos
Connection Pooling
tes instalados para acesso ao Firebird.
pelo Brasil, como TechDay SP,RJ,POA,BH, Web Inicie uma nova aplicação ASP.NET
Days e todas as edições da Borland Conference. no Delphi 2006. A partir da Component

18 ClubeDelphi - Performance no Firebird e ADO.NET

Clube87.indb 18 26.07.07 10:30:10


AS P.NET

ConnectionString. No editor que aparece nada para usar esse recurso, pois ele já é Você pode ainda controlar como o
informe os parâmetros para acesso ao ativado por padrão. Criar um mecanismo ADO.NET trabalha com Connection
banco Employee.fdb do Firebird. de Connection Pooling “no braço” via código Pooling, fazendo alg uns ajustes na
Com isso, configuramos a conexão ao é algo extremamente complicado (infeliz- propriedade ConnectionString do Fb-
Firebird usando o provider nativo, a mente já tive que passar por esse esforço em Connection ou diretamente no editor
primeira dica de performance (jamais uma ocasião). No ADO.NET já temos isso de conexões da Figura 1. Podemos
use OleDB, ODBC ou outro provider pronto no próprio framework. Produtivida- especificar alguns parâmetros, como
nesse caso). de é um dos pontos fortes do .NET. mostra a Tabela 1.
Observe que em User Name e Password Veja um exemplo de como poderíamos
informamos um usuário e senha padrão Nota: O Connection Pooling só pode ser usar alguns desses parâmetros na string
para acesso ao banco. Aqui vai a segunda usado em ambiente multi-thread (uma de conexão da nossa aplicação (você pode
dica valiosa para otimização: forneça um aplicação Web, por exemplo), onde fazer essa modificação diretamente na
usuário e senha fixos, de forma que todos temos várias threads simultâneas pro- propriedade ConnectionString, a partir
os usuários que conectem à aplicação cessando solicitações clientes. Não faz do Object Inspector) com o código da
utilizem as mesmas credenciais. sentido, por exemplo, usar Connection Listagem 1.
Se for necessário restringir acesso a de- Pooling em uma aplicação Windows
terminado usuário, defina isso na forma Forms tradicional (duas camadas). Inter- Atenção: Sempre use a mesma Connection
de autorizações no Web.config. Fornecer namente, o Connection Pooling faz uso String (com os mesmos valores para todos
um usuário fixo fará o ADO.NET utilizar de um interessante recurso do sistema os parâmetros) em todos os objetos de co-
de forma efetiva o recurso de Connection operacional Windows para prover sua nexão, para que compartilhem o mesmo
Pooling, sem ter perda de desempenho. funcionalidade: semáforos. Connection Pooling.
Connection Pooling é o mecanismo que
permite ao ADO.NET reaproveitar co-
nexões ao banco de dados. Imagine a Listagem 1. Parâmetros na string de conexão

seguinte situação: um usuário acessa a User=SYSDBA;Password=masterkey;Database=C:\Program Files\Firebird\Firebird_2_0\examples\


empbuild\EMPLOYEE.FDB;
aplicação, conectamos ao BD para extrair DataSource=LOCALHOST;Port=3050;Dialect=3;Charset=NONE;
informações e a exibimos no formulário. Role=;Packet Size=8192;Server Type=0;Pooling=True;
Min Pool Size=50;Connection Lifetime=120;
A seguir, fechamos a conexão e devolve- Connection timeout=15;

mos o resultado ao browser.


Como aplicações Web são state less (sem
estado), se esse mesmo ou outro usuário Parâmetro Valor Default Descrição
se conectar à aplicação, uma nova cone- Connection Lifetime 0 Tempo de vida, em segundos, que uma conexão deve ficar no pool desde a sua criação.
xão precisará ser restabelecida. Conectar Max Pool Size 100 Número máximo de conexões que podem ficar em pool.
ao BD a cada requisição de usuário é lite- Min Pool Size 0 Número mínimo de conexões que devem ficar em pool.
ralmente um suicídio em ambiente Web, Pooling True Indica se o Connection Pooling está habilitado.
onde uma aplicação pode ter centenas e Tabela 1. Parâmetros do Connection Pooling
até milhares de conexões simultâneas.
O ADO.NET resolve isso de forma bas-
tante elegante: após a página ser enviada
ao browser, a conexão com o BD não é
liberada, mesmo que você tenha chama-
do explicitamente o método Close do Fb
Connection. O ADO.NET guarda automa-
ticamente a conexão em pool (imagine isso
como uma espécie de cache de conexões).
Ou seja, a conexão fica aberta com o
banco de dados e persiste entre requisi-
ções. Quando outro usuário conectar na
aplicação, o ADO.NET verifica se existe
uma conexão disponível no pool e caso en-
contre, a utiliza. Com isso, todo o tempo
necessário para localizar o servidor de
BD, estabelecer uma conexão, autenticar
um usuário e verificar permissões não
será mais consumido a cada requisição.
E o melhor de tudo, você não precisa fazer Figura 1. Parâmetros de conexão ao Firebird

Edição 87 - ClubeDelphi 19

Clube87.indb 19 26.07.07 10:30:16


Usando um DataReader para exibir Mas onde está o FbDataReader? Um Por esse motivo, aplicações que usam
dados em um DataGrid DataReader nunca é instanciado direta- esse componente são conhecidas como
Seguindo nosso exemplo, veremos em mente. Você sempre obterá a referência a “desconectadas”.
um primeiro momento como exibir dados um objeto desse tipo através da chamada Enquanto um DataReader exige que uma
de uma tabela do Firebird em um Data- ao ExecuteReader de um FbCommand. Você conexão esteja ativa para que uma leitura
Grid (coloque um no form), usando nossa pode então usar o objeto de retorno para de dados seja feita, um DataSet pode obter
conexão previamente configurada. varrer o resultset obtido, usando o Read. seus dados uma única vez e guardá-los
A maneira mais rápida de extrair dados Aqui não foi necessário fazer essa var- em memória para uso posterior. Isso
do banco é usando um DataReader (um redura manualmente, visto que usamos garante ainda mais a escalabilidade de
FbDataReader, no caso do provider para o recurso de DataBind do ASP.NET para aplicações ASP.NET. Depende de você
Firebird). Um DataReader é responsável vinculação de dados com o DataGrid, detectar quando é melhor usar um Data-
por fazer a leitura dos dados retornados através da propriedade DataSource. Reader ou um DataSet.
por uma consulta SQL, usando um cursor Observe que todo o código de execução Para ficar mais claro, vamos imaginar
de dados. Ele é rápido pelos seguintes é envolvido em um bloco try...finally. O uma situação: você trabalha para uma
motivos: código dentro do bloco finally sempre universidade e precisa construir a página
• É unidirecional: a navegação pelos será executado, incondicionalmente, de inscrição para o concurso vestibular
registros é feita de forma seqüencial mesmo que uma exceção ocorra (por promovido pela instituição. Nessa pági-
(forward-only). Basicamente lemos um exemplo, se o comando SQL digitado na, existe um formulário para cadastro
registro, fazemos alguma coisa com ele fosse inválido). do candidato, onde constam alguns
(exibimos dados em um controle, por Com isso garantimos que o Close do TextBoxes para entrada de campos como
exemplo) e navegamos para o próximo FbConnection sempre seja chamado após Nome, Endereço, Data Nasc etc.
registro; a execução do código dentro do try, devol- A única informação dinâmica (que vem
• É somente-leitura: não é possível alte- vendo a conexão para o pool. Uma outra de um BD) está em um DropDownList,
rar registros de um DataReader; dica é sempre manter o mínimo de código onde o candidato pode escolher o curso
• Não faz cache: após um registro ser possível entre o Open e o Close, somente o que deseja se inscrever. Essas informa-
lido, ele é descartado da memória. que fizer uso da conexão aberta. ções são obtidas a partir de uma tabela
O código da Listagem 2 mostra como do banco de dados, que possui todos os
usar um DataReader (coloque o código no Usando DataSets em Cache cursos com vagas disponíveis.
Page_Load do Web Form). Aqui abrimos Sem dúvida o principal componente Agora responda uma coisa: para que
a conexão com o banco usando o método do ADO.NET é o DataSet. Ele pode ser conectar ao banco de dados para pre-
Open do FbConnection. A seguir, usamos usado com qualquer um dos providers encher o DropDownList, a cada vez que
um FbCommand para executar um coman- para ADO.NET, representando uma um usuário abre a página, se os cursos
do Select na tabela Employee. estrutura de dados em memória. Aqui o nunca mudam? Em uma situação como
Para executar a consulta, chamamos o termo “memória” se refere ao fato de que essa, a tabela de cursos seria modificada
ExecuteReader do FbCommand (chamado podemos criar uma consulta a uma tabela no BD provavelmente uma vez por ano
aqui de cmd), cujo valor de retorno é atribuí- do banco de dados, extrair informações ou semestre. Esse é um exemplo típico
do à propriedade DataSource do DataGrid. e usá-las após a conexão ser fechada. onde podemos usar um DataSet ao invés
de um DataReader.
Listagem 2. Utilizando o DataReader Vamos ver como usar o recurso na prá-
tica. Inicie uma nova aplicação ASP.NET,
procedure TWebForm1.Page_Load(sender: System.Object; e: System.EventArgs);
var seguindo os mesmos passos do exemplo
cmd: FbCommand;
begin anterior. Coloque um FbDataAdapter no
FbConnection1.Open();
try
formulário e no assistente que abrirá
cmd := FbCommand.Create(‘select * from Employee’, FbConnection1); configure uma conexão ao banco Firebird
DataGrid1.DataSource := cmd.ExecuteReader();
DataGrid1.DataBind(); como fizemos anteriormente. Utilize a
finally
FbConnection1.Close(); seguinte instrução SQL para obter dados
end;
end;
da tabela Departments:
select * from DEPARTMENT
Listagem 3. Código do Page_Load

procedure TWebForm1.Page_Load(sender: System.Object; e: System.EventArgs); Coloque um DropDownList no formulá-


begin
if (not IsPostBack) then
rio e no Page_Load do Web Form digite o
begin código apresentado na Listagem 3.
DropDownList1.DataSource := GetDepartments();
DropDownList1.DataTextField := ‘Department’; Aqui estamos simplesmente testando
DropDownList1.DataValueField := ‘DepartmentId’;
DropDownList1.DataBind(); se a página está sendo carregada pela
end; primeira vez (IsPostBack) para então
end;
inicializar o DropDownList. Observe

20 ClubeDelphi - Performance no Firebird e ADO.NET

Clube87.indb 20 26.07.07 10:30:17


AS P.NET

que atribuímos o valor da propriedade Listagem 4. Função GetDepartments


DataSource para uma função chamada
public
GetDepartments (vista na Listagem 4), que function GetDepartments(): DataSet;
...
retorna um DataSet. Implemente a função function TWebForm1.GetDepartments(): DataSet;
GetDepartments conforme mostrado na var
ds: DataSet;
Listagem 4. begin
if (Cache[‘Department’] = nil) then
No código anterior, testamos se existe begin
ds := DataSet.Create();
na Cache uma variável com o nome que FbDataAdapter1.Fill(ds,’Department’);
estipulamos (“Department”). Se existir, Cache[‘Department’] := ds;
end;
é porque o DataSet já está em memória Result := Cache[‘Department’] as DataSet;
end;
(provavelmente um usuário já realizou a
consulta anteriormente). Caso contrário,
refazemos a cache de dados, chamando o
Fill do FbDataAdapter para preencher os
dados do DataSet. E finalmente, colocamos
o DataSet em memória (objeto Cache).
O Cache é usado no ASP.NET para com-
partilhar dados entre todos os usuários
da aplicação. Mesmo após a requisição
ser processada, as informações perma-
necem na memória do servidor e podem
ser utilizadas posteriormente. Você
pode colocar quantos objetos quiser em
memória, bastando fornecer um nome
diferente para cada um, mas tome cui-
dado para não exagerar na quantidade
de informações que você vai armazenar
no servidor.

Nota: O ASP.NET permite que você use


um state server (servidor de estado), Figura 2. Usando um DataSet em Cache
permitindo que as informações coloca-
das em cache e sessão residam em um
processo diferente do aspnet_wp.exe (o Usando DataViews após o Fill do FbDataAdapter1, que está
processo usado pelo ASP.NET para ro- Vamos aprimorar o exemplo. Vamos no GetDepartments:
dar as aplicações Web, no WinXP). É pos- permitir que o usuário veja os empre-
...
sível, inclusive, especificar um servidor gados relacionados quando escolher um FbDataAdapter2.Fill(ds, 'Employee');

dedicado (que tenha mais memória, por determinado departamento. Ao invés de


exemplo) apenas para armazenar obje- criarmos uma nova consulta ao BD após Agora, quando a página for aberta pela
tos de cache e sessão. Uma outra alter- a seleção, vamos filtrar uma consulta que primeira vez, o DataSet conterá em me-
nativa, é persistir as sessões no próprio já estará residente em memória. Ou seja, mória dados sobre todos os empregados
banco de dados, para poupar memória. vamos guardar todos os empregados em e departamentos do banco de dados.
State Servers também são usados para cache e usar um DataView para filtragem. Tudo o que precisamos fazer é manipular
compartilhar dados de sessão quando Coloque um ListBox no formulário e um essas informações da melhor forma, sem
múltiplos servidores são usados. segundo FbDataAdapter, configurando a a necessidade de consultar o BD a cada
seguinte instrução SQL, que retorna to- requisição.
A Figura 2 mostra a aplicação em exe- dos os registros da tabela de Employee: Por exemplo, para exibir os empregados
cução. Faça um teste, abra um segundo relacionados ao departamento seleciona-
select * from EMPLOYEE
browser e acesse a mesma página, porém do, altere o Page_Load como mostrado na
antes pare o servidor de banco de dados. Ao invés de utilizar um segundo Da- Listagem 5.
Observe que os dados serão mostrados taSet para guardar os produtos, vamos O código é semelhante ao usado para
mesmo que o Firebird esteja desativado, usar o mesmo criado anteriormente. Isso preencher o DropDownList, exceto que
indicando que as informações realmente é possível de ser feito no ADO.NET, basta agora estamos usando um DataView como
não foram obtidas a partir do BD, mas a passar o mesmo DataSet como parâmetro fonte de dados para o controle ListBox.
partir da cache que já estava armazenada para o Fill de ambos os FbDataAdapters. A função GetEmployees recebe o código
na memória do servidor. Inclua então o seguinte código, logo de um departamento por parâmetro e

Edição 87 - ClubeDelphi 21

Clube87.indb 21 26.07.07 10:30:17


retorna os empregados relacionados,
através de um DataView, obtidos a partir
do DataSet de memória. Seu código é
mostrado na Listagem 6.
Um DataView é ideal para se usar com
DataSets de memória. Ele permite que
você filtre e ordene resultsets, fornecendo
diferentes visões de um mesmo conjunto
de dados, sem usar nenhum tipo de ins-
trução SQL ou comunicação extra com
o banco. Vale lembrar que você pode
ter vários DataViews atuando sobre um
mesmo DataSet.
O último passo é configurar o Auto-
PostBack do DropDownList para True,
para que seja feito o postback no servidor
quando o usuário escolher um item no
controle. A Figura 3 mostra o exemplo
em execução.
Figura 3. Usando DataViews a partir de um DataSet em memória
É claro, se os dados forem modificados
no banco de dados, eles não serão refleti-
Listagem 5. Código do Page_Load, atualizado
dos no DataSet da cache. É sua obrigação
procedure TWebForm1.Page_Load(sender: System.Object; e: System.EventArgs);
begin
refazer a cache quando necessário. O
if (not IsPostBack) then ASP.NET possui alguns recursos prontos
begin
{ código do DropDownList } para facilitar esse processo. Podemos,
end
else por exemplo, especificar um critério de
begin
ListBox1.DataSource := GetEmployees(DropDownList1.SelectedValue);
expiração para a cache ou criar um me-
ListBox1.DataTextField := ‘First_Name’; canismo de dependência.
ListBox1.DataBind();
end;
end;
Inserindo com Stored Procedures
Listagem 6. Função GetEmployees Não poderíamos falar em otimizações
de aplicações ASP.NET com ADO.NET
public
function GetEmployees(Dept_No: System.&Object): DataView; sem mostrar a utilização de Stored Pro-
...
function TWebForm1.GetEmployees(Dept_No: TObject): DataView; cedures. Elas aumentam drasticamente
var a velocidade de soluções Web, tanto na
dv: DataView;
begin obtenção de dados do Firebird quanto
dv := DataView.Create();
dv.Table := (Cache[‘Department’] as na atualização, inserção ou exclusão de
DataSet).Tables[‘Employee’] as DataTable;
dv.RowFilter := ‘Dept_No = ‘ + Dept_No.ToString(); registros.
Result := dv;
end;
Isso acontece porque o servidor SQL
pode otimizar planos de execução e pré-
Listagem 7. Stored Procedure para inserção compilar essas instruções que residem
CREATE PROCEDURE COUNTRY_I ( no BD, e não mais na aplicação cliente,
COUNTRY VARCHAR(15),
CURRENCY VARCHAR(10))
que apenas se encarrega de passar os
AS parâmetros apropriados.
BEGIN
INSERT INTO COUNTRY (COUNTRY, CURRENCY) Neste exemplo, construiremos uma
VALUES (:COUNTRY, :CURRENCY);
END aplicação em ASP.NET que fará uso
efetivo de Stored Procedures, de forma a
Listagem 8. Executando a Stored Procedure comprovar o aumento de escalabilidade.
procedure TWebForm1.Button1_Click(sender: TObject; e: System.EventArgs); Primeiramente, no banco de dados, crie
begin
FbConnection1.Open(); a Stored Procedure da Listagem 7, para
try inserir dados na tabela Country.
FbCommand1.Parameters[0].Value := TextBox1.Text;
FbCommand1.Parameters[1].Value := TextBox2.Text; Em uma nova aplicação ASP.NET,
FbCommand1.ExecuteNonQuery();
finally vamos colocar no Web Form alguns
FbConnection1.Close();
end;
TextBoxes para a entrada de dados. Para
end; facilitar, vamos utilizar uma tabela sim-
ples do banco Employee, a tabela Country,

22 ClubeDelphi - Performance no Firebird e ADO.NET

Clube87.indb 22 26.07.07 10:30:19


além de trabalhar apenas com a operação www.devmedia.com.br/clubedelphi/portal.asp

de inserção. Utilizando Labels, Buttons e Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a uma
TextBox, desenhe um formulário igual ao série de vídeo aulas de Guinther Pauli, sobre performances no ASP.NET
da Figura 4. com ADO.NET.
A seguir, coloque um FbConnection e www.devmedia.com.br/articles/listcomp.asp?
txtsearch=Performance+no+ASP.NET+com+ADO.NET
configure uma conexão ao FB como feito
anteriormente. Coloque um FbCommand
e aponte a propriedade Connection para
o FbConnection. Defina seu CommandType
para StoredProcedure e CommandText refe-
rencie a SP criada (“COUNTRY_I”).
Em Parameters inclua dois parâmetros,
como mostra a Figura 5. Observando o que
foi feito para o parâmetro Country, faça o
mesmo para o parâmetro Currency.
No botão Inserir digite o código da Figura 4. Formulário principal da aplicação, com TextBoxes
Listagem 8, para inserir os dados digi-
tados nos TextBoxes no banco de dados, dade, deixando seu usuário final muito
usando a Stored Procedure criada. Note mais satisfeito com o tempo de resposta
que configuramos os parâmetros criados da sua aplicação.
na coleção Parameters do FbCommand. A utilização de Stored Procedures, quan-
ExecuteNonQuery faz a execução da SP do feita de forma apropriada, pode
propriamente dita. garantir o sucesso de sua solução ASP.
NET com ADO.NET, sendo imbatível se
Conclusão comparada a qualquer outra tecnologia
Utilizando as técnicas vistas neste existente. DataSets em memória e Data-
artigo, você poderá agora otimizar suas Readers também são ótimos ingredientes
aplicações ASP.NET para que tenham para turbinar seu Web Site com ASP.NET,
uma melhor performance e escalabili- ADO.NET e Firebird.

Figura 5. Inserindo parâmetros

Clube87.indb 23 26.07.07 10:30:20


Contas a Pagar e Cobrança
Crie um sistema completo com Delphi, Firebird 2.0 e dbExpress - Parte 1

B
em-vindos ao novo mini-curso cadastros que farão parte do exemplo,
da ClubeDelphi, onde o leitor como: Clientes, Fornecedores e Contas
aprenderá como criar um sistema Correntes.
completo de gerenciamento de contas a
pagar e receber, com emissão de boletos Criando o banco de dados
bancários, baixa de pagamentos, fluxo de Para iniciar o projeto, vamos primeira-
caixa, impressão de cheques e uma série mente criar o banco de dados e as tabelas
de outros recursos encontrados nesse necessárias para cada tela de cadastro. As
tipo de sistema. tabelas são: Clientes, Fornecedores e Contas.
O sistema que criaremos terá como prin- Neste artigo, criaremos o banco de dados
cipal objetivo proporcionar ao usuário o utilizando a ferramenta IBExpert em sua
controle total de contas a pagar e a rece- versão Standard. Por isso, acesse o link
ber, ou seja, será possível adicionar contas www.ibexpert.com e em seguida entre no
de luz, telefone, água, gastos com office- item IBExpert.
boy, compra de equipamentos etc. À esquerda do site do fabricante clique
Da mesma forma, o usuário poderá in- em Download>Free. Preencha o formulá-
cluir as faturas que tem a receber de seus rio de cadastro e aguarde o e-mail com as
clientes, processo esse que chamaremos
Adriano Santos de Cobrança. Com essas informações no Metodologia
(artes@doiscliques.com) sistema, sempre atualizadas, poderemos O exemplo deste artigo utiliza a arquitetura cliente/servidor.
é desenvolvedor Delphi desde 1998. Professor
emitir relatórios, gráficos, boletos bancá- Para mais informações sobre essa arquitetura e outras, visite:
e programador PHP. Bacharel em Comunicação
Social pela Universidade Cruzeiro do Sul, SP. É rios e fluxo de caixa para o acompanha- http://www.devmedia.com.br/articles/viewcomp.asp?
colunista e membro da Comissão Editorial da mento das contas correntes. comp=5219
revista ClubeDelphi. Neste artigo, criaremos os principais

24 ClubeDelphi - Contas a Pagar e Cobrança

Clube87.indb 24 26.07.07 10:30:22


M INI -CURS O

instruções de download da ferramenta. Após a criação do banco, podemos e o Position para poScreenCenter. Inclua
Após isso, abra o IBExpert e vamos criar iniciar o desenvolvimento do siste- agora um ImageList (“imgBotoes”), um
o banco de dados usando a opção Script ma, onde usaremos Delphi 7.0, Fire- MainMenu (“mnuPrincipal”) e um Tool-
Executive presente no menu Tools. bird 2.0 e dbExpress. Abra o Delphi e Bar (“tobBotoes”).
Com a tela de scripts aberta, digite o crie um novo projeto através do menu No imgBotoes adicione quatro imagens
código da Listagem 1. Nele estão contidos File>New>Application. a sua escolha representando os botões
os esquemas para a criação do banco de Mude a propriedade Name do formulário Clientes, Fornecedores, Contas e Sair. O
dados assim como de cada tabela do sis- para “frmPrincipal” e salve a unit como mnuPrincipal receberá três itens corres-
tema. Digitado o script, basta executá-lo “uPrincipal.pas”. Mude também a proprie- pondentes aos cadastros que criaremos
usando o botão Run Script ou pressione dade BorderStyle para bsSingle e desative a (Figura 1).
a tecla F9. opção biMaximize em BorderIcons. Faremos nesse instante a configuração
Por fim, modifique o Caption para da barra de ferramentas. Adicione quatro
Nota: Substitua <Caminho>\SYSPAGUE. “Sistema de Contas a Pagar e Cobrança” botões na Toolbar clicando com o botão
FDB pelo diretório do banco de dados e
seu nome, ex: C:\BancoDeDados\SysPa- Listagem 1. Criação do banco, tabelas e índices
gue.fdb. SET SQL DIALECT 3;
SET NAMES WIN1252;

Para este exemplo criamos somente as CREATE DATABASE ‘<Caminho>\SYSPAGUE.FDB’


USER ‘SYSDBA’ PASSWORD ‘masterkey’
tabelas necessárias para os cadastros que PAGE_SIZE 8192
DEFAULT CHARACTER SET WIN1252;
usaremos neste artigo. Note que criamos
em cada tabela os campos DT_ALTERA- CREATE TABLE CLIENTES (
CNPJ VARCHAR(18) NOT NULL,
CAO e STATUS. Esses receberão a data de FANTASIA VARCHAR(20) NOT NULL,
RAZAO VARCHAR(150),
alteração/criação e o estado do registro IE VARCHAR(18) NOT NULL,
respectivamente. ENDERECO VARCHAR(100),
BAIRRO VARCHAR(50),
Esse último terá uma função especial. COMPLEMENTO VARCHAR(30),
CIDADE VARCHAR(50),
Não faremos exclusões no banco de da- ESTADO VARCHAR(2),
CEP VARCHAR(9),
dos. Cada vez que o usuário solicitar a TELEFONE VARCHAR(15),
exclusão de um registro, o mesmo terá FAX
DT_CADASTRO
VARCHAR(15),
TIMESTAMP,
seu STATUS alterado para “I” de inativo STATUS VARCHAR(1),
DT_ALTERACAO TIMESTAMP);
e não mais aparecerá em telas de consul-
CREATE TABLE CONTAS (
tas, cadastros ou relatórios. Dessa forma BANCO INTEGER NOT NULL,
mantemos uma espécie de histórico do AGENCIA VARCHAR(10) NOT NULL,
CONTA VARCHAR(10) NOT NULL,
registro. NOME_AGENCIA VARCHAR(30),
NOME_CONTA VARCHAR(30),
ENDERECO VARCHAR(100),
Formulários da aplicação BAIRRO
COMPLEMENTO
VARCHAR(50),
VARCHAR(30),
As telas de cadastro serão herdadas de CIDADE VARCHAR(50),
ESTADO VARCHAR(2),
um formulário padronizado que será CEP VARCHAR(9),
TELEFONE VARCHAR(15),
criado. Assim, poupamos tempo de de- FAX VARCHAR(15),
DT_CADASTRO TIMESTAMP,
sign e programação, já que boa parte do CONTATO VARCHAR(50),
código e componentes comuns ficarão no STATUS VARCHAR(1),
DT_ALTERACAO TIMESTAMP);
formulário padrão.
CREATE TABLE FORNECEDORES (
No formulário padrão haverá uma CNPJ VARCHAR(18) NOT NULL,
FANTASIA VARCHAR(20) NOT NULL,
caixa para pesquisa no banco de dados e RAZAO VARCHAR(150),
botões comuns para Inclusão, Alteração, IE VARCHAR(18) NOT NULL,
ENDERECO VARCHAR(100),
Exclusão etc. Além dos formulários de BAIRRO VARCHAR(50),
COMPLEMENTO VARCHAR(30),
cadastro, uma tela principal fará a ligação CIDADE VARCHAR(50),
entre as janelas de nosso sistema, assim ESTADO VARCHAR(2),
CEP VARCHAR(9),
poderemos acessar todos os cadastros, re- TELEFONE VARCHAR(15),
FAX VARCHAR(15),
latórios e ferramentas que incluiremos. DT_CADASTRO TIMESTAMP,
STATUS VARCHAR(1),
DT_ALTERACAO TIMESTAMP);

www.devmedia.com.br/clubedelphi/portal.asp ALTER TABLE CLIENTES ADD CONSTRAINT PK_CLIENTES


PRIMARY KEY (CNPJ);

Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a ALTER TABLE CONTAS ADD CONSTRAINT PK_CONTAS
uma vídeo aula de Luciano Pimenta que mostra como baixar e instalar o PRIMARY KEY (BANCO, AGENCIA, CONTA);

IBExpert em sua versão gratuita. ALTER TABLE FORNECEDORES


ADD CONSTRAINT PK_FORNECEDORES PRIMARY KEY (CNPJ);
www.devmedia.com.br/articles/viewcomp.asp?comp=3082

Edição 87 - ClubeDelphi 25

Clube87.indb 25 26.07.07 10:30:29


direito e em seguida em New Button. Mo-
difique a propriedade Images da Toolbar
para o imgBotoes e então altere o Image
Index de cada botão informando o índice
da imagem presente no ImageList.

Nota: Não esqueça de incluir o Caption


de cada botão e ativar a propriedade
ShowCaptions da Toolbar.

Daremos um toque mais profissional


ao projeto incluindo um Panel alinhado
como alClient no formulário principal e
dentro dele (também alClient) um Image.
Nesse Image adicione uma figura usando
a propriedade Picture.
Se necessário modifique o AutoSize e/
ou Stretch esticando ou encolhendo a
Figura 1. MainMenu imagem escolhida. Veja na Figura 2 um
exemplo de tela que desenvolvi. Salve o
projeto com o nome de “SysPague.dpr”.

Desenvolvendo o Data Module


Como desenvolveremos diversas telas
de acesso a dados, precisamos de um Data
Module que contenha as conexões necessá-
rias para tabelas e Stored Procedures. A prin-
cípio não foram criadas Stored Procedures,
porém ao longo do mini-curso criaremos
SPís para manipulação ou seleção de dados
para relatórios ou boletos bancários.
Vamos então iniciar o desenvolvi-
mento do Data Module. Clique em
File>New>Data Module. A primeira pro-
vidência é criar a conexão com o banco
usando um SQLConnection da paleta
dbExpress. Clique duas vezes no compo-
nente e logo em seguida no botão “+”.
Selecione a opção Interbase. Digite um
nome para a conexão e clique em OK.
Retornando ao editor de propriedades,
selecione a opção Database e indique o
caminho e nome do banco de dados criado
anteriormente. Um exemplo disso seria
“localhost:C:\BancoDeDados\SYSPAGUE.
FDB”. Nos parâmetros User_Name e Pas-
sword digite o usuário e senha do banco.
Figura 2. Tela principal da aplicação Clique em OK e marque como False a pro-
priedade LoginPrompt do SQLConnection.
Ainda no Object Inspector modifique o Name
para “sqlConexao”. Modifique a proprie-
dade Name do DM para “dmdPrincipal” e
salve-o como “uDmPrincipal.pas”.
Como dito anteriormente, vamos criar
três telas de cadastro, por isso devemos
criar os componentes de acesso para

26 ClubeDelphi - Contas a Pagar e Cobrança

Clube87.indb 26 26.07.07 10:30:30


M INI -CURS O

todas elas. Começaremos por Clientes, Diferentemente das tabelas já citadas Pesquisas e carregamento de dados
assim adicione no Data Module um (Clientes e Fornecedores) a tabela de Antes de partirmos para o design e pro-
SQLQuery (“sqlClientes”), um DataSetPro- Contas Correntes dificilmente possuirá gramação das telas de cadastro, devemos
vider (“dspClientes”) e um ClientDataSet centenas de registros, por esse motivo nos preocupar com o carregamento dos
(“cdsClientes”). não usaremos a técnica descrita ante- dados, ou seja, como nossas janelas se
As configurações dos componentes são riormente. comportarão e receberão os registros
simples e os passos seguidos aqui podem Nesse ponto do artigo, o Data Module presentes no banco.
ser repetidos para cada grupo de compo- deverá se parecer com a Figura 3. É importante criar funções e/ou proce-
nentes de acesso. No sqlClientes modifique Adicionamos somente os componentes dimentos apropriados para cada consul-
a propriedade SQLConnection escolhendo necessários para a conexão com as três ta. Criaremos as funções CarregarTodos,
o sqlConexao e em sua propriedade SQL tabelas criadas até o momento, ao longo PesquisarCliente, PesquisarFornecedores e
adicione o código da Listagem 2 . do mini-curso adicionaremos novos PesquisarContas, que receberão parâme-
Em seguida clique com o botão direito componentes ao Data Module. tros das telas de cadastro e montarão os
no componente e escolha a opção Fields Agora vamos codificar os eventos After selects necessários para cada situação.
Editor. No editor, clique novamente com Delete, AfterPost e BeforePost de cada Na seção public do Data Module declare
o direito e escolha Add all fields. Isso fará ClientDataSet. Esses eventos terão o papel as funções mencionadas anteriormente
com que todos os campos sejam carre- de chamar o ApplyUpdates e também de como mostrado na Listagem 3.
gados na lista de campos do sqlClientes. atualizar a data de alteração (DT_ALTE- Pressione CTRL+SHIFT+C para que o
No dspClientes apenas modifique a pro- RACAO) da tabela em questão. Para o
priedade DataSet para sqlClientes. Agora evento AfterPost do cdsClientes inclua o
modifique o ProviderName do cdsClientes seguinte código:
informando dspClientes.
cdsClientes.ApplyUpdates(0);
Para terminar nossa primeira configu-
ração, repita os passos de carregamento Já no AfterDelete apenas associe-o ao
dos campos descrito no sqlClientes para AfterPost selecionando-o na lista de
o cdsClientes, ou seja, teremos os campos eventos do componente. Para o BeforePost
carregados no Fields Editor tanto no SQL- digite o código a seguir:
Query quanto no ClientDataSet.
cdsClientes.FieldByName('DT_ALTERACAO').
Repita esses passos para as tabelas de AsDateTime := Now;

Fornecedores e Contas sendo que cada uma


deverá conter o Select adequado. Confira Repita esses passos para os demais
na Tabela 1 o resumo das configurações ClientDataSets, tomando o cuidado de
necessárias, com o nome do componente mudar o nome do objeto. Figura 3. Data Module principal
e o valor da propriedade SQL.
Por motivos de performance, ou seja, Listagem 2. Consulta à tabela de Clientes
para manter a velocidade de carrega-
SELECT
mento dos dados do servidor para o CNPJ, FANTASIA, RAZAO, IE, ENDERECO, BAIRRO,
cliente, nossas queries retornarão sem COMPLEMENTO, CIDADE, ESTADO, CEP,
TELEFONE, FAX, DT_CADASTRO, STATUS, DT_ALTERACAO
registros. Por isso, usamos uma técnica FROM CLIENTES
WHERE CNPJ = -1 AND STATUS = ‘A’
na cláusula where das tabelas de Clientes ORDER BY FANTASIA
e Fornecedores.
Note que a cláusula where foi modifica-
da para CNPJ = -1, assim nenhum registro Componente Propriedade SQL
será retornado, pois não existem empre- SELECT
sas com o CNPJ com o valor -1. A consulta CNPJ, FANTASIA, RAZAO, IE, ENDERECO, BAIRRO, COMPLEMENTO, CIDADE, ESTADO, CEP,
por sua vez será aberta sem registros TELEFONE, FAX, DT_CADASTRO, STATUS, DT_ALTERACAO
sqlFornecedores
evitando carregar centenas de clientes/ FROM FORNECEDORES
fornecedores desnecessariamente. WHERE CNPJ = -1 AND STATUS = ëAí
Para que o usuário altere ou visualize ORDER BY FANTASIA
determinado Cliente/Fornecedor ele SELECT
será obrigado a utilizar-se da caixa de BANCO, AGENCIA, CONTA, NOME_AGENCIA, NOME_CONTA, ENDERECO, BAIRRO, COMPLEMENTO, CIDADE, ESTADO,
pesquisa que criaremos, trazendo o mí- sqlContas
CEP, TELEFONE, FAX, DT_CADASTRO, CONTATO, STATUS, DT_ALTERACAO
nimo possível de registros. Um DBGrid FROM CONTAS
na parte lateral receberá os registros e o WHERE STATUS = ëAí
usuário poderá escolher o cadastro que ORDER BY BANCO, AGENCIA, CONTA
será editado, caso seja necessário. Tabela 1. Comandos SQL dos componentes de acesso a dados

Edição 87 - ClubeDelphi 27

Clube87.indb 27 26.07.07 10:30:33


Listagem 3. Funções para montar as instruções SQL Delphi crie o cabeçalho de cada função
function CarregarTodos(ASqlQuery: TSqlQuery; na área implementation do código-fonte.
ADataSet: TClientDataSet; ATabela, AOrderBy: string): Boolean;
function PesquisarCliente(ACampo, AValor: string; ATipoPesquisa: Integer): Boolean; Vá até o corpo da função CarregarTodos e
function PesquisarFornecedor(ACampo, AValor: string; ATipoPesquisa: Integer): Boolean;
function PesquisarContas(ACampo, AValor: string; ATipoPesquisa: Integer): Boolean;
digite o código da Listagem 4.
A função criada recebe como parâmetro
Listagem 4. Função CarregarTodos os objetos SQLQuery e ClientDataSet que
function TdmdPrincipal.CarregarTodos(ASqlQuery: TSqlQuery; ADataSet: TClientDataSet; serão usados no carregamento dos dados.
ATabela, AOrderBy: string): Boolean;
var
Recebe ainda a tabela e os campos usados
I, J, Conta: Integer; na cláusula order by. Os campos da tabela
ListaCampos: TStrings;
begin são obtidos automaticamente pela função
with ASqlQuery, ADataSet do
begin GetFieldNames do ClientDataSet recebido,
ADataSet.Close;
ListaCampos := TStringList.Create;
e logo em seguida é adicionado à pro-
GetFieldNames(ListaCampos); priedade SQL do SQLQuery, também
Params.Clear;
Sql.Clear; recebido via parâmetro.
Sql.Add(‘SELECT ‘);
for I := 0 to ListaCampos.Count - 1 do Em seguida montamos a cláusula from
if I = ListaCampos.Count - 1 then
Sql.Add(‘ ‘ + ListaCampos[I])
com o nome da tabela e o modo de orde-
else nação. Por fim, atualizamos e abrimos o
Sql.Add(‘ ‘ + ListaCampos[I] + ‘,’);
Sql.Add(‘FROM ‘ + UpperCase(ATabela)); ClientDataSet, devolvendo como resulta-
Sql.Add(‘WHERE STATUS = ‘’A’’ ‘);
{ Monta o Order By } do verdadeiro ou falso o que indica se
if Trim(AOrderBy) <> ‘’ then existem dados no filtro realizado.
begin
Sql.Add(‘ORDER BY’); Agora vamos à função de pesquisa em
Conta := 0;
J := 1; Clientes, localizando o corpo da função
for I := 1 to Length(AOrderBy) do
begin
PesquisarCliente e digitando o código da
Inc(Conta); Listagem 5.
if (AOrderBy[I] = ‘;’) or (I = Length(AOrderBy)) then
begin A função de pesquisa de clientes recebe
if I < Length(AOrderBy) then
Sql.Add(‘ ‘ + Copy(AOrderBy, J, Conta - 1) + ‘,’) como parâmetro o campo onde será rea-
else
Sql.Add(‘ ‘ + Copy(AOrderBy, J, Conta));
lizada a busca, o tipo e o valor. Primeiro
Conta := 0; verificamos o tipo de busca. Caso seja
J := I + 1;
end; EXATA usamos o operador “=”, do con-
end;
end; trário usamos o LIKE.
ListaCampos.Free;
end;
Após isso, fechamos o cdsClientes, lim-
ADataSet.FetchParams; pamos seus parâmetros e atualizamos
ADataSet.Open;
Result := not ADataSet.IsEmpty; a propriedade SQL do SQLClientes com
end;
a novo select para pesquisa. Então, atu-
Listagem 5. Função PesquisarCliente alizamos novamente os parâmetros do
ClientDataset e o abrimos retornando o
function TdmdPrincipal.PesquisarCliente(ACampo, AValor: string; ATipoPesquisa: Integer): Boolean;
var resultado da busca.
Operador: string;
begin As pesquisas de Fornecedores e Contas
case ATipoPesquisa of
0: Operador := ‘=’;
seguem o mesmo raciocínio mudando
1, 2: Operador := ‘LIKE’; apenas os campos, objetos e o nome da
end;
with dmdPrincipal, sqlClientes, SQL do tabela quando necessário (Listagem 6).
begin
cdsClientes.Close; Essas pesquisas serão utilizadas pelas
Clear;
Params.Clear;
janelas de cada cadastro. As funções
Add(‘SELECT ‘); recebem como parâmetro: o campo onde
Add(‘ CNPJ, FANTASIA, RAZAO, IE, ENDERECO, ‘+‘BAIRRO, COMPLEMENTO, CIDADE, ESTADO, ‘);
Add(‘ CEP, TELEFONE, FAX, DT_CADASTRO, STATUS, ‘+‘DT_ALTERACAO ‘); é feita a busca (ACampo), o texto para a
Add(‘FROM ‘);
Add(‘ CLIENTES ‘); pesquisa (AValor) e o tipo de pesquisa
Add(‘WHERE ‘); (ATipoPesquisa) que pode ser: Pesquisa
{ Monta a cláusula Where }
Add(‘(UPPER’ + ACampo + ‘) ‘ + Operador + ‘ :’ + AValor + ‘)’); Exata, A partir do Início ou Em qualquer
cdsClientes.FetchParams;
case ATipoPesquisa of lugar. Dessa forma podemos localizar
0: cdsClientes.Params.ParamByName(AValor).AsString := UpperCase(AValor);
1: cdsClientes.Params.ParamByName(AValor).AsString := UpperCase(AValor) + ‘%’;
um registro digitando, por exemplo, a
2: cdsClientes.Params.ParamByName(AValor).AsString := ‘%’ + UpperCase(AValor) + ‘%’; razão social completa, o início dela ou
end;
Add(‘ AND STATUS = ‘’A’’ ‘); simplesmente pesquisando em qualquer
Add(‘ORDER BY ‘);
Add(‘ FANTASIA ‘); lugar do campo RAZAO.
cdsClientes.Open;
Result := not cdsClientes.IsEmpty;
A função CarregarTodos tem o objetivo de
end; carregar todos os registros da tabela, caso
end;
o usuário necessite de uma visualização

28 ClubeDelphi - Contas a Pagar e Cobrança

Clube87.indb 28 26.07.07 10:30:34


M INI -CURS O

geral. Lembrando apenas de ter cuidado Listagem 6. Funções PesquisarFornecedores e PesquisarContas


ao utilizar essa abordagem, já que pode- function TdmdPrincipal.PesquisarFornecedor(
mos ter milhares de registros na tabela, o ACampo, AValor: string;
ATipoPesquisa: Integer): Boolean;
que implicaria em uma demora bastante var
Operador: string;
grande no carregamento dos dados. begin
case ATipoPesquisa of
Outra informação importante é que 0: Operador := ‘=’;
estamos utilizando os mesmos compo- 1, 2: Operador := ‘LIKE’;
end;
nentes de acesso a dados para utilização with dmdPrincipal, sqlFornecedores, SQL do
begin
nas pesquisas. Isso significa que a cada cdsFornecedores.Close;
nova pesquisa realizada, a propriedade Clear;
Add(‘SELECT ‘);
SQL dos SQLQuerys são sobrescritas e Add(‘ CNPJ, FANTASIA, RAZAO, IE, ENDERECO, ‘+
‘BAIRRO, COMPLEMENTO, CIDADE, ESTADO, ‘);
reabertas para filtrar os dados. Add(‘ CEP, TELEFONE, FAX, DT_CADASTRO, STATUS, ‘+
‘DT_ALTERACAO ‘);
Uma dica seria a criação de componen- Add(‘FROM ‘);
tes separados para cadastro e pesquisa, Add(‘ FORNECEDORES ‘);
Add(‘WHERE ‘);
ou em alguns casos, até Data Modules { Monta a cláusula Where }
Add(‘(UPPER’ + ACampo + ‘) ‘ + Operador +
diferentes. ‘ :’ + AValor + ‘)’);
cdsFornecedores.FetchParams;
case ATipoPesquisa of
Tela de cadastro padrão 0: cdsFornecedores.Params.ParamByName(
AValor).AsString := UpperCase(AValor);
Vamos desenvolver a parte visual de 1: cdsFornecedores.Params.ParamByName(
AValor).AsString := UpperCase(AValor) + ‘%’;
cada tela para que possamos visualizar 2: cdsFornecedores.Params.ParamByName(
AValor).AsString := ‘%’ + UpperCase(AValor) +
como o exemplo ficará. Para isso, usare- ‘%’;
mos herança, então criaremos um formu- end;
Add(‘ AND STATUS = ‘’A’’ ‘);
lário padrão e dele desenvolveremos as Add(‘ORDER BY ‘);
Add(‘ FANTASIA ‘);
telas necessárias para este exemplo. cdsFornecedores.Open;
Result := not cdsFornecedores.IsEmpty;
Para isso, crie um novo formulário end;
usando a opção File>New>Form. Mude end;

seu Name para “frmPadrao” e salve-o function TdmdPrincipal.PesquisarContas(


ACampo, AValor: string;
como “uPadrao.pas”. As telas de cadas- ATipoPesquisa: Integer): Boolean;
var
tros devem parecer com a Figura 4. Operador: string;
À esquerda temos um painel de pes- begin
case ATipoPesquisa of
quisa que será usado para pesquisar os 0: Operador := ‘=’;
1, 2: Operador := ‘LIKE’;
registros na base de dados e disponibi- end;
lizarmos para consulta e/ou alteração. with dmdPrincipal, sqlContas, SQL do
begin
Já que não carregaremos todos os dados cdsContas.Close;
Clear;
na abertura da janela, precisaremos de Params.Clear;
Add(‘SELECT’);
recursos para busca, ou seja, vamos evitar Add(‘ BANCO, AGENCIA, CONTA, NOME_AGENCIA, ‘+
que o usuário carregue muitos registros ‘NOME_CONTA, ENDERECO,’);
Add(‘ BAIRRO, COMPLEMENTO, CIDADE, ESTADO, CEP, ‘+
em tela o que causaria lentidão e tráfego ‘TELEFONE, FAX,’);
Add(‘ DT_CADASTRO, CONTATO, STATUS, ‘+
desnecessário na rede. ‘DT_ALTERACAO’);
Add(‘FROM’);
Ao final da tela temos três Labels e três Add(‘ CONTAS’);
DBTexts. Esses DBTexts estão vinculados Add(‘WHERE ‘);
{ Monta a cláusula Where }
aos campos DT_CADASTRO, STATUS e Add(‘(UPPER(‘ + ACampo + ‘) ‘ + Operador +
‘ :’ + AValor + ‘)’);
DT_ALTERACAO presente em cada ta- cdsContas.FetchParams;
case ATipoPesquisa of
bela. Eles são responsáveis por informar 0: cdsContas.Params.ParamByName(
a data que o registro foi criado, o estado AValor).AsString := UpperCase(AValor);
1: cdsContas.Params.ParamByName(
atual dele e sua última alteração. AValor).AsString := UpperCase(AValor) + ‘%’;
2: cdsContas.Params.ParamByName(
AValor).AsString := ‘%’ + UpperCase(AValor) +
‘%’;
end;
www.devmedia.com.br/clubedelphi/portal.asp
Add(‘ AND STATUS = ‘’A’’ ‘);
Add(‘ORDER BY’);
Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a uma Add(‘ BANCO, AGENCIA, CONTA’);
vídeo aula de Everson Volaco que mostra como trabalhar com herança cdsContas.Open;
Result := not cdsContas.IsEmpty;
visual de formulários no Delphi. end;
end;
www.devmedia.com.br/articles/viewcomp.asp?comp=1716

Edição 87 - ClubeDelphi 29

Clube87.indb 29 26.07.07 10:30:35


Esses campos serão atualizados auto-
maticamente pelo sistema através dos
eventos AfterPost e AfterInsert de cada
ClientDataSet como vimos na fase de
criação do Data Module.
Como configuração visual, temos ao
topo uma ToolBar e dentro dela um DBNa-
vigator (“nvtNavegador”), sete ToolButtons
e alguns separadores. Temos também
um ImageList com as imagens usadas nos
botões mencionados.
Um detalhe bastante importante no
formulário padrão é o DataSource que
teremos que incluir. Ele será responsável
por conectar-se ao ClientDataSet cor-
respondente à tabela. Com ele também
faremos uso de sua propriedade DataSet
para que possamos chamar as funções Figura 4. Exemplo de tela de cadastro
de manipulação dos dados, tais como
inserção, alteração, exclusão etc.
Vamos tratar agora de fazer a codifi- botão iniciando pelos botões btnNovo, zamos o btnVisualizarTodos. Mostraremos
cação dos botões que terão as funções btnEditar, btnSalvar, btnCancelar e btn uma mensagem de confirmação e caso o
básicas do cadastro: Incluir, Alterar, Sal- Excluir. O código completo encontra-se usuário aceite, o sistema faz um select no
var, Cancelar, Excluir, Visualizar Todos na Listagem 8. banco e traz para a aplicação quantos fo-
e Fechar. Note que a codificação é bastante rem os registros na base. A montagem do
simples dispensando explicações mais painel de pesquisa consiste em inserir um
Nota: Modifique o Name de cada botão detalhadas a respeito, pois são apenas Panel (“pnlPesquisa”) alinhado alLeft.
da barra como a seguir: “btnNovo”, “btn comandos comuns de manipulação de Modifique também sua propriedade
Editar”, “btnSalvar”, “btnCancelar”, “btn DataSets. O importante é notar que não BevelOuter para bvNone. Dentro do pnl-
Excluir”,“btnVisualizarTodosí e “btnSair”. estamos fazendo as chamadas aos méto- Pesquisa adicione dois ComboBoxes com
dos diretamente nos objetos presentes no os nomes de “cbxPesquisa” e “cbxTipo-
Antes de tudo crie uma nova procedure Data Module principal. Busca” respectivamente. Inclua também
conforme o seguinte código: Todos os métodos são utilizados através um Edit (“edtPesquisa”) e um SpeedButton
do dstGenerico, isso porque o formulário (“spbPesquisa”). Alinhado como alBottom
procedure AtivarBotoes(AEdicao: Boolean);
será herdado quando criarmos os cadas- coloque um DBGrid.
Esse procedimento fará a ativação/ tros. Os botões mais importantes nesse
desativação dos botões da barra de fer- ponto são btnVisualizarTodos e spbPesquisa Nota: Modifique também a proprieda-
ramentas, bem como outros elementos (no painel de pesquisa). Eles não recebem de DataSource do nvtNavegador e DB-
da tela. Pressione CTRL+SHIFT+C para código porque serão personalizados e Grid ligando-os ao dstGenerico.
que o Delphi crie o cabeçalho do proce- adaptados a cada situação.
dimento. No corpo da procedure escreva Como mencionei, não permitiremos No primeiro cbxPesquisa, apenas configu-
o código da Listagem 7. que todos os registros sejam carregados re para csDropDownList a propriedade Style.
Os elementos que ainda não vimos até de início na tela. Por isso, o usuário será Os itens serão preenchidos usando o Ge-
aqui e que constam em AtivarBotoes são: obrigado a fazer uma pesquisa no painel tFieldNames do DataSet no evento OnShow
• nvtNavegador: esse é o DBNavigator usa- e localizar o registro desejado. Isso será do formulário, por isso digite o código da
do para navegar nos registros da tabela; feito utilizando-se os métodos de pesqui- Listagem 9 no evento mencionado.
• pnlPesquisa: à esquerda temos um Panel sa criados no Data Module principal. No cbxTipoBusca inclua agora os itens
onde encontram-se os campos para pes- Mesmo usando esse conceito, caso o “Exata”, “A partir do início” e “Em qual-
quisa. usuário queira forçar o carregamento de quer lugar” e modifique a propriedade
Após isso, faremos a codificação de cada todos os registros da tabela, disponibili- Style como csDropDownList. Com isso fi-

30 ClubeDelphi - Contas a Pagar e Cobrança

Clube87.indb 30 26.07.07 10:30:37


M INI -CURS O

nalizamos a criação do nosso formulário Listagem 7. Código da procedure AtivarBotoes

padrão, agora basta adicionarmos ele ao procedure TfrmPadrao.AtivarBotoes(AEdicao: Boolean);


begin
repositório do Delphi. nvtNavegador.Enabled := not AEdicao;
tbnNovo.Enabled := not AEdicao;
Para isso, clique com o botão direito tbnEditar.Enabled := not AEdicao;
no formulário padrão e escolha a opção tbnExcluir.Enabled := not AEdicao;
pnlPesquisa.Enabled := not AEdicao;
Add to Repository. Com a caixa de diálogo tbnSair.Enabled := not AEdicao;
tbnVisualizarTodos.Enabled := not AEdicao;
aberta, digite em Title o título do formu- tbnSalvar.Enabled := AEdicao;
lário padrão que salvaremos, nesse caso tbnCancelar.Enabled := AEdicao;
end;
“FormPadrao”.
Coloque uma descrição em Description Listagem 8. Código dos botões de manipulação do banco de dados
e escolha a página Forms no item Page procedure TfrmPadrao.tbnNovoClick(Sender: TObject);
(Figura 5). begin
AtivarBotoes(True);
if not dstGenerico.DataSet.Active then
dstGenerico.DataSet.Open;
Nota: Caso não queira adicionar o for- dstGenerico.DataSet.Append;
dstGenerico.DataSet.FieldByName(‘DT_CADASTRO’).AsDateTime := Now;
mulário explicitamente ao repositório, dstGenerico.DataSet.FieldByName(‘STATUS’).AsString := ‘A’;
basta acessá-lo a partir da aba com o end;

nome do projeto (SysPague) que apare- procedure TfrmPadrao.tbnEditarClick(Sender: TObject);


begin
ce automaticamente no repository. Uma if dstGenerico.DataSet.IsEmpty then
Exit;
vantagem de adicionar o form dessa AtivarBotoes(True);
forma que fizemos é que ele fica dispo- dstGenerico.DataSet.Edit;
end;
nível para outros projetos, além de rece-
procedure TfrmPadrao.tbnSalvarClick(Sender: TObject);
ber os textos descritivos e ícone. begin
AtivarBotoes(False);
dstGenerico.DataSet.Post;
end;

Cadastro de clientes procedure TfrmPadrao.tbnCancelarClick(Sender: TObject);


begin
Observe que as tabelas de Clientes e AtivarBotoes(False);
dstGenerico.DataSet.Cancel;
Fornecedores são exatamente iguais, mu- end;
dando apenas seu nome (uma alternativa
procedure TfrmPadrao.tbnExcluirClick(Sender: TObject);
seria criar uma tabela só, mas não foi a begin
if dstGenerico.DataSet.IsEmpty then
opção que escolhi). Exit;
if MessageDlg(‘Confirma exclusão do ‘+‘registro atual?’, mtConfirmation, [mbYes, mbNo],
Portanto a parte visual também deverá 0) = mrYES then
ser igual. Sendo assim vamos utilizar o re- begin
dstGenerico.DataSet.Edit;
curso de repositório do Delphi para evitar dstGenerico.DataSet.FieldByName(‘STATUS’).AsString := ‘I’;
dstGenerico.DataSet.Post;
redesenhar várias vezes a mesma tela. dstGenerico.DataSet.Close;
Para utilizar o repositório clique em dstGenerico.DataSet.Open;
end;
File>New>Other. Escolha a guia Forms, end;

selecione o FormPadrao, marque a opção


Listagem 9. Carregando ComboBox com os campos
Inherit e confirme. Nesse ponto, criamos
uma “cópia” perfeita do formulário pa- procedure TfrmPadrao.FormShow(Sender: TObject);
begin
drão incluindo os eventos (herança). cbxPesquisa.Items.Clear;
dstGenerico.DataSet.GetFieldNames(cbxPesquisa.Items);
O que devemos fazer agora é codificar cbxPesquisa.ItemIndex := 0;
AtivarBotoes(False);
os botões spbPesquisa e btnVisualizarTodos, end;
além de inserir os DBEdits ligando-os a
cada campo da tabela. Salve o formulário
como “uClientes.pas” e mude seu Name
para “frmClientes”.
Nosso formulário é uma cópia herdada
de padrão, por isso, será necessário con-
figurar a propriedade DataSet do dstGe-
nerico apontando-o para o cdsClientes no
Data Module principal.

Nota: Não esqueça de selecionar


File>Use unit no Delphi e incluir a unit
dmdPrincipal ao uses do formulário de
Clientes. Figura 5. Adicionando o formulário ao Repositório

Edição 87 - ClubeDelphi 31

Clube87.indb 31 26.07.07 10:30:40


Após conectar o dstGenerico ao cdsClien- Feito isso, o cadastro de Clientes estará ra 6, já com cada DBEdit apontando para
tes, vamos incluir o campo FANTASIA quase completo. Vamos digitar o código o respectivo DataSource e DataField.
ao DBGrid de pesquisa. Para isso, clique dos botões spbPesquisar e btnVisualizarTo-
duas vezes no DBGrid e em seguida dos (Listagem 10). Cadastro de Fornecedores
clique no primeiro botão da caixa de O Pesquisar consiste em chamar a fun- Para o cadastro de Fornecedores basta
diálogo que se abre. ção PesquisarCliente que criamos no Data seguir todos os passos usados para a
Em FieldName escolha FANTASIA. Esse Module principal e verificar se existem criação da tela de Clientes. É necessário
passo deverá ser repetido nas janelas de dados no resultado. Já o CarregarTodos faz apenas atentar aos detalhes cruciais
Fornecedores e Contas Correntes e é ne- um select no banco de dados e traz para a dessa etapa:
cessário, pois não adicionamos nenhum aplicação todos os registros encontrados • Ligar o dstGenerico ao ClientDataSet
campo ao DBGrid quando desenhamos o na tabela passada como parâmetro. correto, nesse caso o cdsFornecedores;
formulário padrão, de onde herdamos. A tela final deve ser semelhante à Figu- • Fazer a chamada a PesquisarFornecedor
no spbPesquisa;
• Configurar o código do btnVisualizar-
Listagem 10. Código dos botões btnPesquisar e btnVisualizarTodos
Todos.
procedure TfrmClientes.spbPesquisaClick(Sender: TObject);
begin Salve o formulário como “uFornecedo-
inherited; res.pas” e troque seu Name para “frmFor-
if (Trim(edtPesquisa.Text) = ‘’) or (Length(edtPesquisa.Text) < 4) then
begin necedores”.
MessageDlg(‘Digite ao menos 4 (quatro) letras ‘+
‘para realizar a pesquisa.’, mtInformation, [mbOk], 0);
edtPesquisa.SetFocus;
end
Cadastro de contas correntes
else Nessa etapa, criaremos o último ca-
begin
with dmdPrincipal do dastro deste exemplo: Contas Correntes.
begin
if not PesquisarCliente(cbxPesquisa.Items[cbxPesquisa.ItemIndex], edtPesquisa.Text, Para poupar tempo vamos utilizar a
cbxTipoBusca.ItemIndex) then
MessageDlg(‘Nada selecionado para filtro.’, mtInformation, [mbOk], 0);
tela padrão de cadastro criada em nosso
end; repositório. Conforme mostrado ante-
end;
end; riormente, adicione um novo formulário
procedure TfrmClientes.tbnVisualizarTodosClick(Sender: TObject); usando o mesmo método da criação
begin
inherited;
dos formulários Clientes e Fornecedores,
if MessageDlg(‘Essa operação poderá demorar ‘+‘alguns minutos dependendo do volume de dados.’ + salve-o como “uContas.pas” e mude seu
#13 + ‘Deseja prosseguir?’, mtConfirmation, [mbYes, mbNo], 0) = mrYes then
with dmdPrincipal do Name para “frmContas”.
if not CarregarTodos(sqlClientes, cdsClientes, ‘CLIENTES’, ‘FANTASIA’) then
MessageDlg(‘Nada selecionado para filtro.’, mtInformation, [mbYes, mbNo], 0); Os botões btnVisualizarTodos e spbPes-
end; quisar farão chamadas a CarregarTodos e
PesquisarConta, respectivamente. Os códi-
gos para ambos são os mesmos utilizados
nas telas anteriores, atentando-se apenas
as funções utilizadas e aos parâmetros a
serem utilizados em cada situação.
No DBGrid de pesquisa, adicione apenas
o campo NOME_CONTA. Estão faltando
apenas os DBEdits ligados aos campos da
tabela em questão. Basta ligá-los utilizan-
do as propriedades DataSource e DataField
como já estamos acostumados a fazer.
O layout de tela sugerido para este ca-
dastro pode ser encontrado na Figura 7.
Pronto, com isso concluímos a criação
de todos os cadastros. Aproveito pra fazer
um comentário em relação ao Excluir e
sua relação com o campo Status criado na
tabela. Se observar, no evento OnClick do
btnExcluir perceberá que não excluímos
efetivamente o registro e sim alteramos
o seu Status para “I” de inativo.
Dessa forma mantemos o registro sem-
pre na tabela, de maneira que podemos
Figura 6. Cadastro de Clientes resgatá-lo caso seja necessário. Para evitar

32 ClubeDelphi - Contas a Pagar e Cobrança

Clube87.indb 32 26.07.07 10:30:44


M INI -CURS O

que o mesmo seja visualizado em telas de


cadastro ou mesmo em relatórios não pode-
mos esquecer de incluir na cláusula where
da consulta, a verificação Status = ‘A’.
É isso que fazemos em todas as consul-
tas realizadas neste exemplo.

Concluindo o exemplo
Chegamos ao final do exemplo e
precisamos agora apenas de alguns
retoques para finalizar todas as etapas.
Pressione CTRL+SHIFT+F11 ou acesse
o menu Project>Options. Entre na guia
Forms e selecione todos os formulários,
com exceção do Data Module e da tela
principal, e passe-os para a lista Available
Figura 7. Exemplo de tela de Contas Correntes
forms, ou seja, retire-os da lista de criação
automática.
Ainda nessa tela, mova o Data Module
Listagem 11. Chamada para as telas de cadastro
para cima de maneira que seja o primeiro
uses uClientes, uFornecedores, uContas;
a carregar junto com o executável. Na aba ...
Application, em Title, digite “SysPague – procedure TfrmPrincipal.tbnClientesClick(Sender: TObject);
begin
Contas a Pagar e Cobrança”. Escolha um frmClientes := TfrmClientes.Create(Self);
try
ícone se preferir e confirme. frmClientes.ShowModal;
Para finalizar, na tela principal, faça finally
frmClientes.Free;
a chamada para cada tela de cadastro end;
end;
nos botões e menus correspondentes. O
procedure TfrmPrincipal.tbnContasClick(Sender: TObject);
código completo para a chamada de cada begin
item encontra-se na Listagem 11. frmContas := TfrmContas.Create(Self);
try
frmContas.ShowModal;
finally
Conclusão frmContas.Free;
end;
Nessa primeira etapa do mini-curso end;
sobre Sistema de Contas a Pagar e Co-
procedure TfrmPrincipal.tbnFornecedoresClick(Sender: TObject);
brança, criamos as três primeiras telas begin
frmFornecedores := TfrmFornecedores.Create(Self);
de cadastro necessárias para a manipu- try
frmFornecedores.ShowModal;
lação de todos os dados do sistema. Nas finally
próximas etapas criaremos os demais frmFornecedores.Free;
end;
cadastros e entraremos nas fases de ma- end;

nipulação dos dados.

Edição 87 - ClubeDelphi 33

Clube87.indb 33 26.07.07 10:30:45


Impressão em ECF
Como imprimir em impressoras fiscais

N
este artigo aprenderemos e a adquirir e se enquadrar com o esquema
entenderemos melhor como de emissão de cupom fiscal.
funciona um ECF (Emissor de Os cupons fiscais são emitidos através
Cupom Fiscal). Vamos ainda desenvolver do ECF, uma impressora fiscal de nature-
um pequeno programa capaz de impri- za matricial ou térmica. Essas impresso-
mir em impressora fiscal. ras coletam as vendas diárias emitindo ao
Para iniciarmos nosso projeto, preci- final do dia a Redução Z, para fechamento
samos primeiramente entender o que ou troca de operador, e a Leitura X para
exatamente é um ECF e como funciona. início do período de vendas. Tanto a
Para isso vamos recorrer ao CONVÊNIO Redução Z quanto a Leitura X devem ser
ECF 01/98. Nele estão dispostas a regras mantidas próximas ao ECF para fins de
básicas para emissão do cupom fiscal. fiscalização quando solicitadas.
Conforme mencionado no convênio: O ECF deve ser conectado a um PC ou
“Os estabelecimentos que exerçam a ativi- PDV (Ponto de Vendas) compacto. Como
dade de venda ou revenda de mercadorias ou mencionado, a prática é utilizada para o
bens, ou de prestação de serviços em que o desenvolvimento de sistemas de vendas
adquirente ou tomador seja pessoa física ou ao consumidor. Normalmente, utili-
jurídica não contribuinte do imposto estadual, zamos impressoras fiscais em lojas de
Adriano Santos estão obrigados ao uso de equipamento emis- conveniência, lanchonetes, bares e uma
(artes@doiscliques.com)
sor de Cupom Fiscal - ECF.” infinidade de outros estabelecimentos
é desenvolvedor Delphi desde 1998. Professor
e programador PHP. Bacharel em Comunicação Isso significa que todo e qualquer esta- comerciais.
Social pela Universidade Cruzeiro do Sul, SP. É belecimento que efetue qualquer tipo de A interação entre o sistema e a impres-
colunista e membro da Comissão Editorial da venda ou revenda ao consumidor com sora fiscal, é feita através das portas
revista ClubeDelphi. as características citadas, são obrigados seriais, ou COM, por meio de bibliotecas

34 ClubeDelphi - Impressão em ECF

Clube87.indb 34 26.07.07 10:30:49


MÃO NA MAS S A

de link dinâmico, DLLs. Existem diversos uma breve explicação sobre a utilização ser vista no script da Listagem 1.
fabricantes de impressoras fiscais, entre desse emulador.
eles a Bematech (www.bematech.com.br). Descompacte o arquivo e execute o Nota: Substitua <Caminho>\ECF.fdb
Alguns tipos de contribuintes ficam programa de instalação (Install Emul2100 pelo diretório do banco de dados e seu
isentos da utilização dos ECFís tais como v1.01.msi). Após a instalação um grupo nome, ex: C:\BancoDeDados\ECF.fdb.
empresas com faturamento inferior a chamado Bematech aparecerá no seu
R$ 120.000,00 anuais, empresas que não menu Iniciar>Programas e nele o atalho
utilizem sistemas de processamento de para o emulador.
dados ficam isentas de utilizar o ECF, Abra o programa e clique com o botão
estabelecimentos de ensino (Portaria direito, assim terá acesso a todos os
91/02) e corretoras. controles do emulador tais como: ligar/
Para uma listagem completa consulte desligar, porta serial, sensores etc. (Fi-
o link wiki.stoq.com.br/wiki/ECF_FAQs gura 2).
que contém uma série de informações Além disso, será necessário efetuar o
necessárias para o uso ou não do ECF. download da biblioteca BemaFI32.dll para
Uma série de regras são estabelecidas que possamos desenvolver a aplicação
para a utilização correta do equipamento exemplo, portanto, no mesmo link aces-
tais como: sado, localize e baixe o arquivo BemaFI32.
• Somente é aceito o cancelamento de zip. Crie uma pasta onde salvaremos
um cupom fiscal se o mesmo foi imedia- nosso exemplo e descompacte o arquivo
tamente cancelado após sua emissão; nessa pasta. Figura 1. Cupom Fiscal impresso
• O cupom fiscal não deve ser emitido Notará que, além da biblioteca, outros
em caso de operações ou prestações com arquivos serão descompactados no
empresas contribuintes de ICMS; diretório, tais como arquivos de help e
• O cupom fiscal deve conter número do configurações. Precisamos apenas dos
item, descrição, valor, código do produto arquivos BemaFI32.dll e BemaFI32.ini. Para
vendido, quantidade, bem como valor usar o emulador, com nosso exemplo, é
pago pelo consumidor, troco e forma de necessário alterar a chave EmulMFD de
pagamento. “0” para “1”, por isso, abra o arquivo Be-
Veja na Figura 1 um exemplo de Cupom maFI32.ini e localize a chave informada.
Fiscal impresso (emitido pelo emulador Altere para “1” e salve.
da impressora fiscal), com os produtos
vendidos, total, valor pago e troco. Criando o banco de dados de
exemplo
Emulando a impressora Vamos criar duas tabelas simples em
Neste exemplo, vamos utilizar a im- um banco de dados Firebird. Teremos a
pressora térmica fiscal MP-2100 TH FI da tabela PRODUTOS, com os produtos que
Bematech (emulador). A Bematech dispo- serão vendidos e VENDAS, nossa tabela
nibiliza, gratuitamente, o emulador da de pedidos. A estrutura de ambas pode Figura 2. Emulador da impressora fiscal
impressora que pode ser utilizado para
testar o equipamento e o sistema criado Listagem 1. Criação do banco de dados e tabelas
para esse propósito. Além do emulador,
SET SQL DIALECT 3;
uma série de exemplos podem ser baixa- SET NAMES WIN1252;
CREATE DATABASE ‘localhost:<Caminho>\ECF.fdb’
dos no site do fabricante. USER ‘SYSDBA’
Entre no site www.bematech.com.br e PAGE_SIZE 8192
DEFAULT CHARACTER SET WIN1252;
acesse o menu superior Suporte. Logo
CREATE TABLE PRODUTOS (
em seguida clique no link Downloads ID INTEGER NOT NULL,
DESCRICAO VARCHAR(30) NOT NULL,
e Drivers e após em Impressoras Fiscais. UNITARIO NUMERIC(15,2) NOT NULL);
No ComboBox que aparece, selecione a
CREATE TABLE VENDAS (
sistema operacional Windows e aguarde PEDIDO INTEGER NOT NULL,
ITEM INTEGER NOT NULL,
o carregamento da página. ID_PRODUTO INTEGER NOT NULL,
DESCRICAO VARCHAR(30) NOT NULL,
Localize Emul2100 - VAREJO (versão QTDE INTEGER NOT NULL,
1.01) e baixe o arquivo Emul2100v101.zip. UNITARIO NUMERIC(15,2) NOT NULL);

Se preferir poderá baixar também o ar- ALTER TABLE PRODUTOS ADD CONSTRAINT PRODUTOS_PK
PRIMARY KEY (ID);
quivo FilmeEmul2100.zip que contém um ALTER TABLE VENDAS ADD CONSTRAINT VENDAS_PK
arquivo de vídeo no formato WMV com PRIMARY KEY (PEDIDO, ITEM, ID_PRODUTO);

Edição 87 - ClubeDelphi 35

Clube87.indb 35 26.07.07 10:30:51


Data Module incluindo o código a seguir: tos” e “dspVendas”), ambos ligados aos
Com o banco de dados criado, vamos seus respectivos SQLDataSets através da
SELECT ID, DESCRICAO, UNITARIO FROM PRODUTOS
criar a aplicação. Abra o Delphi, crie uma ORDER BY ID propriedade DataSet.
nova aplicação e salve a mesma. Vamos Adicione dois ClientDataSets (“cdsPro-
criar um Data Module e fazer as conexões Em sdsVendas inclua o select a seguir: dutos” e “cdsVendas”) e modifique a
necessárias para nosso exemplo. Clique propriedade ProviderName de cada um,
SELECT ID, ITEM, ID_PRODUTO, DESCRICAO, QTDE,
em File>New>Data Module. Salve-o como UNITARIO conectando-os a dspProdutos e dspVendas.
FROM VENDAS WHERE PEDIDO = -1
“udmPrincipal.pas” e mude seu Name ORDER BY PEDIDO, ITEM, ID_PRODUTO
Assim como nos SQLDataSets, inclua
para “dmdPrincipal”. todos os campos da tabela.
Vamos fazer uma conexão típica com Por último, adicione dois DataSources
dbExpress, para isso, adicione um SQL- Após isso, clique duas vezes em cada (“dstProdutos” e “dstVendas”) e ligue-os
Connection (“sqlConexao”) e dois SQLDa- SQLDataSet e adicione todos os campos aos ClientDataSets pela propriedade Data-
taSets (“sdsProdutos” e “sdsVendas”). Cli- da tabela usando a opção Add all fields. Set. Feito isso vamos inserir um campo do
que duas vezes no sqlConexao e crie uma Inclua dois DataSetProviders (“dspProdu- tipo InternalCalc nos Fields do cdsVenda.
nova conexão apontando para o banco Esse campo será usado para calcular
ECF.fdb que criamos anteriormente. automaticamente o valor de cada item
www.devmedia.com.br/clubedelphi/portal.asp
Marque como False a propriedade Login- adicionado ao pedido. Para isso, clique
Prompt. Em seguida modifique a proprie- Acesse agora o portal do assinante ClubeDelphi e assista a uma vídeo duas vezes no cdsVendas e no editor,
dade SQLConnection de cada SQLDataSet aula de Guinther Pauli que explica como trabalhar com InternalCalc em clique com o botão direito e escolha New
ClientDataSets. Aula que faz parte do mini curso de ClientDataSet do autor.
apontando-os para o sqlConexao. Modi- Field. Em Name coloque “VALOR_TO-
www.devmedia.com.br/articles/viewcomp.asp?comp=5734
fique a propriedade SQL do sdsProdutos TAL”, escolha o tipo Float e marque-o
como InternalCalc (Figura 3).
Nesse momento devemos programar
os eventos AfterPost e AfertDelete de cada
ClientDataSet incluindo uma chamada
ao ApplyUpdates. Isso facilita na hora de
gravar os registros incluídos em cada
operação dispensando o ApplyUpdates
em tela.
Entre no evento AfterPost do cdsProdutos
e inclua o código:
cdsProdutos.ApplyUpdates(0);

Após isso, ligue o evento AfterDelete


ao AfterPost codificado anteriormente.
Repita esses passos com o cdsVendas
mudando somente o objeto que receberá
o método, como:
cdsVendas.ApplyUpdates(0);

Precisamos agora programar os eventos


OnDestroy e OnCreate do Data Module. A
Figura 3. Criando um campo calculado do tipo InternalCalc
codificação é simples. Apenas abrimos
e fechamos os ClientDataSets na criação
e destruição do Data Module, respec-
tivamente. Veja o código completo dos
eventos na Listagem 2.
A última providência a se tomar é
codificar o evento OnCalcFields do cds-
Vendas. Isso é necessário, pois o campo
VALOR_TOTAL criado deverá receber o
valor total de cada item, ou seja, o valor
unitário multiplicado pela quantidade
de produtos selecionada pelo operador.
Para isso adicione o código a seguir, ao
Figura 4. Data Module evento citado:

36 ClubeDelphi - Impressão em ECF

Clube87.indb 36 26.07.07 10:30:52


MÃO NA MAS S A

cdsVendas.FieldByName('VALOR_TOTAL').AsFloat := nalize como desejar. Para finalizar esse adicione um ComboBox (“cbxFormPagto”).
cdsVendas.FieldByName('QTDE').AsFloat *
cdsVendas.FieldByName('UNITARIO').AsFloat; GroupBox, insira mais dois Labels, um Em sua propriedade Items inclua os itens:
com o Caption “SubTotal” e o outro com “Dinheiro”, “Cheque”, “Vale” e “Duplica-
O Data Module deve se parecer com a o Name para “lblSubTotal”. ta”. Em seguida insira um Edit (“edtVa-
Figura 4. A área que acabamos de criar será res- lorPago”) e um BitBtn (“btnPagar”) e por
ponsável por todo o processo de criação fim, inclua um novo BitBtn maior com o
Desenvolvendo a tela principal do pedido, ou seja, será possível criar o nome de “btnFinalizarCupom”.
Nossa aplicação terá a interface da pedido, incluir e excluir produtos, visu-
Figura 5. Na parte superior da tela colo- alizar o Subtotal, escolher a quantidade Comunicação com a Impressora
que um GroupBox. Adicione também um de produtos a ser incluído e ainda iniciar usando a DLL
MaskEdit (“edtCPFCNPJ”). Configure a o fechamento da venda. Para codificar nosso exemplo precisa-
propriedade EditMask com a máscara A última etapa de desenvolvimento mos antes declarar explicitamente todas
“999.999.999-99;0;_” e mude o tamanho da parte visual do exemplo é criar o as funções da impressora fiscal presente
da fonte para “18”. rodapé do formulário onde teremos as da biblioteca BemaFI32.dll para que possa-
Ao lado adicione um SpeedButton operações de escolha do tipo de paga- mos usar normalmente seus métodos.
(“btnAbrirCupom”) e à direita um Label mento, o valor pago pelo consumidor e Neste exemplo não usaremos todas as
(“lblMsgOperador”). Mude o tamanho da a finalização do pedido. funções presentes na biblioteca, apenas
fonte para “18” e a cor para clBlue. Marque Coloque um novo GroupBox e dentro o necessário. Por isso, abra o editor de
como False a propriedade AutoSize e True
em WordWrap.
Listagem 2. Eventos OnDestroy e OnCreate do Data Module
Aumente a largura e altura do lblMsgO-
perador de forma que se adapte melhor ao procedure TdmdPrincipal.DataModuleDestroy(
Sender: TObject);
espaço deixado à direita do formulário. begin
cdsProdutos.Close;
Inclua um novo GroupBox com o Caption cdsVendas.Close;
end;
“Dados do Produto e Pedido”. Nele
adicione um DBGrid (“dbgProdutos”) e procedure TdmdPrincipal.DataModuleCreate(
Sender: TObject);
ligue-o ao cdsProdutos pela propriedade begin
cdsProdutos.Open;
DataSource. cdsVendas.Open;
end;

Nota: Para que o formulário principal


enxergue o Data Module, não esqueça
de adicioná-lo à seção uses do formulá-
rio usando o menu File>Use unit.

Clique duas vezes no DBGrid e adicione


todos os campos da tabela PRODUTOS.
Configure as colunas, cores e fontes como
desejar. À direita do dbgProdutos, adicione
três novos botões que deverão se chamar
“btnAdicionarItem”, “btnCancelarItem” e
“btnFecharVenda”. Modifique os Captions
para “Adicionar Produto”, “Cancelar Item
Anterior” e “Fechar e Totalizar”.
Abaixo, insira um Label com o Caption
“Quantidade” e ao lado um SpinEdit
(“speQtde”). À direita, dois novos Labels,
onde no primeiro apenas modifique seu
Caption para “Pedido” e no segundo
mude seu Name para “lblPedido”.
Adicione também um novo SpeedBut-
ton (“speCancelarItemSelecionado”) e
mude seu Caption para “Cancelar Item
Selecionado”. Modifique sua largura
se necessário. Insira um novo DBGrid
(“dbgVendas”) e aponte-o para cdsVendas.
Adicione os campos da tabela e perso- Figura 5. Interface de aplicação

Edição 87 - ClubeDelphi 37

Clube87.indb 37 26.07.07 10:30:54


códigos do Delphi e declare todas as venda e ativa/desativa componentes cedimentos. RetornarPedido cria em tempo
funções da Listagem 3. Declare-as acima visuais; de execução uma nova conexão e executa
da palavra reservada implementation, ou • AtualizarMensagem: Atualiza o lblMsg uma select na tabela de VENDAS usando
seja, na área de interface. Operador informando mensagens ao ope- Max para descobrir o maior número de
Na Tabela 1 temos as finalidades das rador do ECF. pedido gerado. Logo após incrementá-lo,
funções descritas anteriormente, que Vá até a seção public do formulário e retorna o próximo pedido (uma opção
são utilizadas para comunicar-se, atra- declare as funções e procedimentos como seria usar um Generator/Sequence do
vés de vínculo estático, com as APIs da a seguir: Firebird).
BemaFI32.dll que por sua vez envia os Já InicializarVariaveis e AtualizarMensagem
function RetornarPedido: Integer;
comandos ao ECF. procedure InicializarVariaveis; têm a responsabilidade de ativar e desa-
procedure AtualizarMensagem(AMensagem:
string);
tivar botões, zerar variáveis e atualizar
Funções auxiliares as mensagens enviadas ao operador por
Será necessário também criar algumas Aproveite e declare também duas va- meio do lblMsgOperador, como dito ante-
funções auxiliares pra evitar a digitação riáveis: riormente. Localize os procedimentos e
repetida de procedimentos importantes iPedido: Integer; codifique-os conforme a Listagem 4, onde
SubTotal: Double;
no projeto: temos todos os códigos necessários.
• RetornarPedido: Retorna o número do
próximo pedido a ser criado; Pressione CTRL+SHIFT+C para que o Abertura de cupom
• InicializarVariaveis: Inicia uma nova Delphi crie o cabeçalho de todos os pro- Agora vamos iniciar a codificação de
cada botão da interface. O exemplo con-
siste em abrir um novo cupom, função do
Listagem 3. Declaração das funções da biblioteca btnAbrirCupom. Em seguida o operador
{ Funções da BEMAFI32.dll } deve escolher o item e a quantidade e
function Bematech_FI_AbreCupom(
CGC_CPF: string): Integer; stdCall; adicionar/excluir ao pedido.
external ‘BEMAFI32.DLL’; Por último é necessário informar o tipo
function Bematech_FI_VendeItem(Codigo: string;
Descricao: string; Aliquota: string; de pagamento e o valor a ser pago fechan-
TipoQuantidade: string; Quantidade: string;
CasasDecimais: Integer; ValorUnitario: string; do a venda. Após isso, o exemplo volta ao
TipoDesconto: string; Desconto: string): Integer;
stdCall; external ‘BEMAFI32.DLL’;
estado de stand by, ou seja, aguardando
function Bematech_FI_IniciaFechamentoCupom( nova venda. Para iniciar o cupom digite
AcrescimoDesconto: string;
TipoAcrescimoDesconto: string; o código da Listagem 5 no OnClick do
ValorAcrescimoDesconto: string): Integer;
stdCall; external ‘BEMAFI32.DLL’; btnAbrirCupom.
function Bematech_FI_TerminaFechamentoCupom(
Mensagem: string): Integer; StdCall;
O código chama o método que inicializa
external ‘BEMAFI32.DLL’; as variáveis e habilita os botões necessá-
function Bematech_FI_EfetuaFormaPagamento(
FormaPagamento: string; rios para a venda. A abertura do cupom
ValorFormaPagamento: string): Integer; stdCall;
external ‘BEMAFI32.DLL’; se dá pela chamada à Bematech_FI_Abre-
function Bematech_FI_CancelaCupom: Integer; stdCall;
external ‘BEMAFI32.DLL’;
Cupom informando, em seu único parâ-
function Bematech_FI_CancelaItemAnterior: Integer; metro, o CPF ou CNPJ do cliente.
stdCall; external ‘BEMAFI32.DLL’;
function Bematech_FI_CancelaItemGenerico(
NumeroItem: string): Integer; stdCall;
external ‘BEMAFI32.DLL’; Adicionando itens ao cupom e
cancelando
Antes de iniciarmos a venda, devemos
Função Descrição
incluir os produtos ao pedido. Na fase de
Bematech_FI_AbreCupom Responsável por iniciar a venda. criação do banco de dados criamos apenas
Bematech_FI_VendeItem
Usada para enviar e imprimir o item vendido a impressora. A cada novo item vendido as tabelas, índices e o banco em si, porém
deverá obrigatoriamente ser enviado para impressão. não temos registros nas tabelas.
Inicia o fechamento do cupom fiscal, ou seja, nesse ponto a venda o cupom fiscal é Entre no seu programa preferido para
Bematech_FI_IniciaFechamentoCupom totalizado aguardando apenas a escolha da forma de pagamento e a entrada do valor gerenciamento da base de dados (sugiro o
para pagamento. IBExpert) e insira alguns produtos na tabe-
Bematech_FI_TerminaFechamentoCupom Finaliza definitivamente a venda e prepara o ECF para uma nova venda. la para que possamos testar a aplicação.
Bematech_FI_EfetuaFormaPagamento Envia para a impressora o valor e a forma de pagamento.
Bematech_FI_CancelaCupom Cancela a venda. www.devmedia.com.br/clubedelphi/portal.asp
Bematech_FI_CancelaItemAnterior Cancela o último item enviado ao ECF.
Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a
Cancela um item específico informado pelo parâmetro recebido. O parâmetro recebe o uma vídeo aula de Luciano Pimenta que mostra como baixar e instalar o
Bematech_FI_CancelaItemGenerico
número do item a ser cancelado. IBExpert em sua versão gratuita.
Tabela 1. Funções da BemaFI32.dll que serão usadas no exemplo www.devmedia.com.br/articles/viewcomp.asp?comp=3082

38 ClubeDelphi - Impressão em ECF

Clube87.indb 38 26.07.07 10:30:55


MÃO NA MAS S A

Os botões btnAdicionarProduto e btn- Listagem 4. Código de RetornarPedido, InicializarVariaveis e AtualizarMensagem


CancelarProduto têm a função de incluir/ function TfrmPrincipal.RetornarPedido: Integer;
excluir o item da tabela VENDAS bem var
sqlTemporario: TSQLDataSet;
como no cupom fiscal. Portanto, basta begin
with dmdPrincipal do
fazer chamada aos métodos Append, De- begin
lete ou Post do cdsVendas e os respectivos sqlTemporario := TSQLDataSet.Create(nil);
sqlTemporario.SQLConnection := sqlConexao;
métodos da impressora, como podemos with sqlTemporario do
begin
ver nas Listagens 6 e 7. CommandText := ‘SELECT MAX(PEDIDO) AS PEDIDO FROM VENDAS’;
Open;
if FieldByName(‘PEDIDO’).AsInteger = 0 then
Nota: Os parâmetros Alíquota e Tipo- Result := 1
else
Quantidade indicam a alíquota de im- Result := FieldByName(‘PEDIDO’).AsInteger + 1;
end;
posto (ICMS ou ISS) e, como o próprio sqlTemporario.Free;
end;
nome indica, tipo de quantidade. No end;
primeiro podemos informar 4 ou 5 ca-
racteres já com o valor da alíquota: ex
procedure TfrmPrincipal.InicializarVariaveis;
1800 ou 18,00 (com vírgula) ou ainda begin
SubTotal := 0;
o índice da alíquota gravada na im- iPedido := RetornarPedido;
pressora que pode ser “I” ou “II”. Se for btnAbrirCupom.Enabled := False;
btnAdicionarItem.Enabled := True;
o índice da alíquota deve ser 2 caracte- btnCancelarProduto.Enabled := False;
btnFecharVenda.Enabled := False;
res. Ex. (18,00 para o valor ou 05 para o btnPagar.Enabled := False;
índice). O TipoQuantidade pode ser in- btnFecharCupom.Enabled := False;
cbxFormasPgto.Enabled := False;
formado como “I” para Inteira e “II” pra cbxFormasPgto.ItemIndex := 0;
edtValorPago.Enabled := False;
fracionária. edtValorPago.Text := FormatFloat(‘###,###,##0.00’, 0);
speQtde.Value := 1;
speQtde.Enabled := True;
Note que adicionamos o item na tabela lblPedido.Caption := FormatFloat(‘0000’, iPedido);
AtualizarMensagem(‘Iniciando Venda’);
VENDAS, como também fizemos uma lblSubTotal.Caption := FormatFloat(‘###,###,##0.00’, SubTotal);
edtValorPago.Text := FormatFloat(‘#####0.00’, SubTotal);
chamada à Bemateh_FI_VendeItem pas- edtCPFCNPJ.Enabled := False;
cbxFormasPgto.ItemIndex := 0;
sando como parâmetro os dados do item end;
vendido. Essa abordagem é obrigatória,
procedure TfrmPrincipal.AtualizarMensagem(AMensagem: string);
ou seja, todo e qualquer item vendido begin
Application.ProcessMessages;
deve ser enviado ao ECF para ser conta- lblMsgOperador.Caption := AMensagem;
lblMsgOperador.Update;
bilizado e totalizado. end;
Note também que na ação do Cancelar
item anterior chamamos outra função do Listagem 5. Código de abertura do Cupom

ECF: Bematech_FI_CancelaItemAnterior, ou procedure TfrmPrincipal.btnAbrirCupomClick(Sender: TObject);


begin
seja, o processo inverso a venda do item. InicializarVariaveis;
Novamente o item em questão deve ser Bematech_FI_AbreCupom(PChar(edtCPFCNPJ.Text));
end;
contabilizado, nesse caso cancelado pelo
emissor de cupom fiscal.

Edição 87 - ClubeDelphi 39

Clube87.indb 39 26.07.07 10:30:56


Listagem 6. Código do Adicionar Produto
Fechamento do cupom
Adicionados os produtos, nosso ope-
procedure TfrmPrincipal.btnAdicionarItemClick(Sender: TObject);
var
rador deverá fechar / totalizar o pedido
Item: Integer; e aguardar a escolha da forma de paga-
Produto: string;
Descricao: string; mento e o pagamento em si. Nesse ponto
Qtde: string;
Unitario: string; devemos codificar o botão responsável
begin
with dmdPrincipal, cdsVendas do
pela primeira ação: btnFecharVenda.
begin Clicando no botão em questão, cha-
Last;
Item := FieldByName(‘ITEM’).AsInteger + 1; mamos o Bematech_FI_IniciaFechamento
Append;
FieldByName(‘PEDIDO’).AsInteger := iPedido; Cupom que inicia o fechamento do
FieldByName(‘ITEM’).AsInteger := Item;
FieldByName(‘ID_PRODUTO’).AsInteger := cdsProdutos.FieldByName(‘ID’).AsInteger;
cupom. Nesse ponto, a impressora já
FieldByName(‘DESCRICAO’).AsString := cdsProdutos.FieldByName(‘DESCRICAO’).AsString; exibe o valor total da venda e aguarda
FieldByName(‘QTDE’).AsInteger := speQtde.Value;
FieldByName(‘UNITARIO’).AsFloat := cdsProdutos.FieldByName(‘UNITARIO’).AsFloat; a forma de pagamento e o valor pago
Post;
btnCancelarProduto.Enabled := True; (Listagem 8).
btnFecharVenda.Enabled := True;
btnCancelarItemEspecifico.Enabled := True;
speQtde.Value := 1; Nota: Observe que cada botão clicado
Subtotal := Subtotal + cdsVendas.FieldByName(‘VALOR_TOTAL’).AsFloat;
lblSubtotal.Caption := FormatFloat(‘###,###,##0.00’, SubTotal); pelo operador, botões são ativados/
edtValorPago.Text := FormatFloat(‘#####0.00’, SubTotal);
AtualizarMensagem(‘Item adicinoado’); desativados em seqüência para que o
mesmo siga uma lógica na venda dos
{ Adiciona o item ao ECF, consequentemente, vendendo-o. Formata os dados necessários
para a venda } itens. Uma operação depende da outra.
Produto := FormatFloat(‘0000000000’, FieldByName(‘ID_PRODUTO’).AsInteger);
Descricao := FieldByName(‘DESCRICAO’).AsString;
Qtde := IntToStr(FieldByName(‘QTDE’).AsInteger);
Unitario := FloatToStr(cdsVendas.FieldByName(‘UNITARIO’).AsFloat);
Estamos chegando ao fim do desenvol-
AtualizarMensagem(Descricao); vimento do sistema. Precisamos apenas
{ Função utilizada para realizar a venda de um item } codificar os botões btnPagar e btnFechar-
Bematech_FI_VendeItem(PChar(Produto), PChar(Descricao), ‘II’, ‘I’, PChar(Qtde), 2,
PChar(Unitario), ‘%’, ‘0,00’); Cupom. No btnPagar nosso principal proce-
end;
end;
dimento é a chamada Bematech_FI_Efetua
FormaPagamento.
Ela recebe a forma de pagamento e o va-
Listagem 7. Código do Cancelar Produto
lor informado. Exemplo: Pagamento em
procedure TfrmPrincipal.btnCancelarProdutoClick(Sender: TObject);
begin
Dinheiro no valor de R$ 200,00. Isso fará
with dmdPrincipal do com que o ECF calcule o total do cupom
begin
Subtotal := Subtotal - cdsVendas.FieldByName(‘VALOR_TOTAL’).AsFloat; e o troco que será dado se necessário
lblSubtotal.Caption := FormatFloat(‘###,###,##0.00’, SubTotal);
edtValorPago.Text := FormatFloat(‘#####0.00’, SubTotal); (Listagem 9).
cdsVendas.Delete;
btnCancelarProduto.Enabled := not cdsVendas.IsEmpty;
Agora basta chamarmos o Bematech
btnCancelarItemEspecifico.Enabled := not cdsVendas.IsEmpty; _FI_TerminaFechamentoCupom presente
AtualizarMensagem(‘Item cancelado’);
{ Cancela também o item anterior do ECF } na Listagem 10 enviando a mensagem
Bematech_FI_CancelaItemAnterior;
end; final ao ECF. Nesse ponto a impressora
end; fiscal finaliza a venda e se prepara para
nova emissão.
Listagem 8. Código do Fechar e Totalizar Um último método, bastante importan-
procedure TfrmPrincipal.btnFecharVendaClick(Sender: TObject); te, é o código do Cancelar item selecionado.
var
Produto: string; Descricao: string; Qtde: string;
Esse botão pode ser utilizado caso o clien-
Unitario: string; AcrescimoDesconto: string; te deseje cancelar a compra de um item
TipoAcrescimoDesconto: string;
ValorAcrescimoDesconto: string; em específico. Nesse caso podemos fazer
begin
with dmdPrincipal do uma chamada ao Bematech_FI_Cancela
begin ItemGenerico passando como parâmetro
with cdsVendas do
begin o número do item a ser cancelado.
AcrescimoDesconto := ‘A’;
TipoAcrescimoDesconto := ‘%’; Em nosso exemplo foi passado o valor
ValorAcrescimoDesconto := ‘0,00’;
Bematech_FI_IniciaFechamentoCupom(PChar(AcrescimoDesconto),
do campo ITEM da tabela VENDAS do
PChar(TipoAcrescimoDesconto), PChar(ValorAcrescimoDesconto)); registro (produto) selecionado. Dessa
speQtde.Enabled := False;
btnAdicionarItem.Enabled := False; forma, basta o operador selecionar no
btnCancelarProduto.Enabled := False;
btnFecharVenda.Enabled := False; DBGrid o item a ser cancelado e clicar no
cbxFormasPgto.Enabled := True;
edtValorPago.Enabled := True;
botão. O código de cancelamento especí-
btnPagar.Enabled := True; fico é parecido com o código do Cancelar
end;
end; item anterior. Veja na Listagem 11.
end;

40 ClubeDelphi - Impressão em ECF

Clube87.indb 40 26.07.07 10:31:01


MÃO NA MAS S A

Testando o ECF Listagem 9. Código do botão Pagar


Com isso finalizamos a codificação do
procedure TfrmPrincipal.btnPagarClick(Sender: TObject);
exemplo. É importante lembrar que não var
colocamos codificações extras no fonte ValorPago: string; FormaPgto: string;
begin
tais como: tratamentos de valores, trata- if (StrToInt(FormatFloat(
edtValorPago.Text, 0)) <> 0) then
mento de envio das funções à impressora, begin
btnFecharCupom.Enabled := True;
checagem de valores obrigatórios etc. É lblMsgOperador.Caption := ‘’;
importante validar todas as entradas de Update;
case cbxFormasPgto.ItemIndex of
dados para que não cause erros na emis- 0: FormaPgto := ‘Dinheiro’;
1: FormaPgto := ‘Cheque’;
são do cupom fiscal. 2: FormaPgto := ‘Vale’;
Para testar nosso exemplo abra primei- 3: FormaPgto := ‘Duplicata’;
end;
ramente o emulador e clique com o botão ValorPago := FormatFloat(‘###,###,##0.00’, StrToFloat(edtValorPago.Text));
Bematech_FI_EfetuaFormaPagamento(PChar(FormaPgto), PChar(ValorPago));
direito do mouse e “ligue” a impressora. cbxFormasPgto.Enabled := False;
edtValorPago.Enabled := False;
Siga as instruções do emulador para btnFecharCupom.Enabled := True;
iniciar. Agora abra o sistema e efetue end
else
todos os procedimentos para emissão do begin
MessageDlg(‘Valor não pode ser igual a zero!’, mtWarning, [mbOk], 0);
cupom fiscal. edtValorPago.SetFocus;
end;
end;
Nota: Não foi incluído neste exem-
plo um botão para cancelamento Listagem 10. Código de finalização

do Cupom fiscal, porém essa ope- procedure TfrmPrincipal.btnFecharCupomClick(Sender: TObject);


var
ração é simples bastando chamar Mensagem: string;
Bematech_FI_CancelaCupom. begin
try
Screen.Cursor := crHourGlass;
AtualizarMensagem(‘Finalizando venda’);
Veja na Figura 6 nosso sistema execu- Mensagem := ‘Obrigado, volte sempre!’;
AtualizarMensagem(Mensagem);
tando em conjunto com o emulador. Bematech_FI_TerminaFechamentoCupom(PChar(Mensagem));
InicializarVariaveis;
btnAbrirCupom.Enabled := True;
Nota: Ao ligar o emulador, caso apareça edtCPFCNPJ.Enabled := True;
edtCPFCNPJ.Text := ‘’;
alguma mensagem sobre erro de porta, edtCPFCNPJ.SetFocus;
dmdPrincipal.cdsVendas.Close;
não se preocupe. Verifique a mensagem dmdPrincipal.cdsVendas.Open;
do emulador, siga os passos e o emula- finally
Screen.Cursor := crDefault;
dor funcionará normalmente. end;
end;

Listagem 11. Cancelamento de item específico


Conclusão procedure TfrmPrincipal.btnCancelarItemEspecificoClick(Sender: TObject);
Neste artigo vimos como desenvolver begin
with dmdPrincipal do
facilmente um Emissor de Cupom Fiscal, begin
usando como exemplo a biblioteca Bema- Subtotal := Subtotal - cdsVendas.FieldByName(‘VALOR_TOTAL’).AsFloat;
lblSubtotal.Caption := FormatFloat(‘###,###,##0.00’, SubTotal);
FI32.dll e o emulador fornecido pelo fa- edtValorPago.Text := FormatFloat(‘#####0.00’, SubTotal);
{ Primeiro cancela o item específico do ECF }
bricante. Para um maior aprofundamento Bematech_FI_CancelaItemGenerico(IntToStr(cdsVendas.FieldByName(‘ITEM’).AsInteger));
{ Agora apaga da pré-venda }
dos fundamentos de ECF recomendo cdsVendas.Delete;
efetuar o download de exemplos no pró- btnCancelarProduto.Enabled := not cdsVendas.IsEmpty;
btnCancelarItemEspecifico.Enabled := not cdsVendas.IsEmpty;
prio site do fabricante, onde é possível AtualizarMensagem(‘Item cancelado’);
end;
encontrar diversas informações a respeito end;
de impressão fiscal.
Até a próxima.

Links

Site do fabricante da Impressora Fiscal MP-2100 TH FI


www.bematech.com.br
FAQ com as principais perguntas e respostas sobre ECF
wiki.stoq.com.br/wiki/ECF_FAQs

Edição 87 - ClubeDelphi 41

Clube87.indb 41 26.07.07 10:31:01


Figura 6. Sistema em funcionamento

42 ClubeDelphi - Impressão em ECF

Clube87.indb 42 26.07.07 10:31:04


Clube87.indb 43 26.07.07 10:31:12
Segurança de Sistemas
Controle de acesso e níveis de usuários

F
alar em segurança de sistemas, uso dos componentes do pacote.
logo faz lembrar vários itens, que Ao final do artigo, também veremos
precisamos tomar os mais minu- como gerar eventos no programa (log),
ciosos cuidados, para que o sistema seja podendo filtrar a consulta de log por
realmente seguro. Neste artigo veremos usuário, data e nível.
como definir de forma segura controles
de acessos diferenciados para usuários de O componente
sistemas desenvolvidos em Delphi para Faremos o download do componente a
a plataforma Win32. partir do site mantido pelo desenvolvedor
Faremos uso do User Control Package, e colaboradores do projeto. Acessando o
um pacote open source de componentes, endereço www.usercontrol.net encontra-
que podem ser facilmente instalados nas remos um amplo canal de informações,
versões posteriores ao Delphi 5, permi- dicas, fórum, entre outros, relacionados
tindo conexão aos principais padrões de aos componentes do pacote.
acessos a dados e servidores de banco de Faça o download do pacote através da
dados compatíveis com Delphi. O acesso seção destinada no site. Para este artigo,
pode ser definido individualmente por faremos o download da versão User Control
Usuário e por Perfil de usuários, dando a 2.20 RC2. Fique a vontade para fazer o down-
Maikel Marcelo Scheid opção de acompanhar uma lista de todos load das outras versões disponíveis. Salve o
(maikelscheid@gmail.com) os usuários logados no sistema. arquivo compactado dentro da pasta Lib do
é técnico em Informática com ênfase em Análi-
Iniciaremos com a instalação do compo- seu Delphi (ou escolha outra pasta).
se e Programação de Sistemas. Atua na área de
Desenvolvimento de Softwares em Delphi para nente, passando então à criação de uma Após o download, descompacte o arqui-
plataforma win32 e .NET com banco de dados aplicação Delphi com acesso a um banco vo zipado, deixando os fontes na pasta
Firebird e MS SQL há 3 anos. de dados Firebird, onde aplicaremos o UserControl.

44 ClubeDelphi - Segurança de Sistemas

Clube87.indb 44 26.07.07 10:31:13


SEGUR ANÇ A

Instalando o componente Abra o mesmo e no Project Manager Instalando os conectores


Usaremos neste artigo para fazer a clique sobre a BPL dando um Build (Fi- Para que possamos realizar os contro-
instalação, configuração e uso do com- gura 1). les de usuários e níveis de permissões,
ponente, o Delphi 2006, mas você poderá Repita o processo com os pacotes precisamos instalar os componentes
usar livremente qualquer outra versão pckUCDataConnector.dpk, pckUserControl_ responsáveis pela comunicação do User
superior ao Delphi 5. A instalação do User RT.dpk e depois pckUserControl_DT.dpk. Control com os componentes de acesso
Control é simples, no menu File>Open do Apenas no último, pckUserControl_ a dados. Juntamente com os arquivos
Delphi, localize, dentro da pasta UserCon- DT.dpk, após o Build, com o botão direito de instalação, dentro da pasta Packages,
trol/Packages, o arquivo pckMD5.dpk. sobre o pacote escolha a opção Install. encontraremos uma pasta Connectors.
Uma mensagem de confirmação de Para cada um dos principais tipos
sucesso de instalação do componente suportado pelo Delphi, teremos um co-
será apresentada. Na mensagem também nector a ser instalado. Tomaremos neste
serão exibidos os componentes que você artigo como exemplo o acesso ao banco de
acabou de instalar (Figura 2). dados Firebird, onde faremos a instalação
Depois de instalado o componente, con- do pacote UCDBXConn.
firme a mensagem e use o menu File>Save Se preferir, você poderá instalar todos
All para concretizar a instalação. os demais conectores, assim, estará ha-
bilitado para qualquer tipo de conexão.
Para fazer a instalação é muito simples,
www.devmedia.com.br/clubedelphi/portal.asp
semelhante a instalação do componente,
Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a uma basta abrir o pacote que será encontrada
vídeo aula de Paulo Quicoli que mostra como instalar os componentes na pasta do pacote, Build e após Install.
User Control no Delphi.
Novamente, você será notificado do su-
Figura 1. Instalação dos pacotes do componente www.devmedia.com.br/articles/viewcomp.asp?comp=5675
cesso da instalação do conector e poderá
salvar a instalação.

Nota: Lembre-se de adicionar à Library


Path do Delphi (Tools>Options) o cami-
nho da pasta com o fonte do compo-
nente, para que o mesmo possa fazer
referência durante o uso.

Figura 2. Mensagem de confirmação após instalação do componente

Figura 3. Criando o Data Module da aplicação

Edição 87 - ClubeDelphi 45

Clube87.indb 45 26.07.07 10:31:21


Iniciando uma aplicação
Criaremos agora uma aplicação no
Delphi, onde utilizaremos um formu-
lário principal e um Data Module para
configuração do componente de acesso
a dados. Iniciando uma nova aplicação
VCL Forms Application (File>New>VCL
Forms Application), criaremos uma pasta
chamada “UCApp” onde salvaremos os
arquivos da aplicação.
Salve o projeto com o nome de “User-
ControlApp.dpr” e o formulário como
“UntUCApp.pas”. Altere o Caption do
formulário para “User Control” e o nome
para “frmPrincipal”. Através do menu
File>New>Other>Delphi Files, selecione
Data Module e confirme (Figura 3). Altere
o nome do mesmo para “DMConexao” e
salve como “UntDMConexao.pas”.

Configurando a conexão Figura 4. Configurando o acesso ao bando de dados Employee.fdb


Adicione ao DMconexao um SQLCon-
nection, altere seu nome para “Conexao” Componente Utilização do componente
e com um duplo clique do mouse sobre o Principal componente do pacote. Todas as funções de controle serão centralizadas no mesmo. Ligue a
componente, abra o assistente para con- UserControl propriedade Data Connector ao UCDBXConn que está no DMConexao.Veremos mais adiante as configurações
figuração do acesso ao banco de dados. mais detalhadas desse componente.
Crie uma nova conexão, selecione o driver Permite a customização do UserControl. Podemos alterar textos e mensagens de telas de formulários do
UCSettings
Interbase e em Connection Name dê o nome componente.
de “EMPLOYEE”. Confirme e proceda Envia e-mails ao usuário, como por exemplo, para a opção Esqueci a senha. Ligue a propriedade
MailUserControls
com as configurações do acesso. MailUserControl do UserControl para esse componente.
Utilizaremos o banco EMPLOYEE.fdb Controla a inatividade do usuário. Poderá ser configurado um TimeOut para que o próprio sistema efetue
UCIdle
que acompanha a instalação do Firebird, logoff. Ligue a propriedade UserControl ao UserControl1.
e configuramos o User_Name, Password Tabela 1. Componentes adicionados e função desempenhada por cada um
e ServerCharSet (Figura 4). Altere a pro-
priedade LoginPrompt para False e ative Arquivo Usuários Log
a conexão. Alterar Senha (“MSenha”)
Logoff (“MLogoff”) Cadastrar Usuários (“MUser”)
Log do Sistema (“MLog”)
Sair (“MSair”) Perfis de Usuários (“MPerfil”)
Usuários Logados (“MLogados”)
Tabela 2. Itens do menu

Propriedade Menu da aplicação


User>MenuItem MUser: Acessado pelo menu Cadastro de Usuários
UserPasswordChange>MenuItem MSenha: Acessado pelo menu Alterar Senha
UserProfile>MenuItem MPerfil: Acessado pelo menu Perfis de Usuários
UsersLogged>MenuItem MLogados: Acessado pelo menu Usuários Logados
LogControl>MenuItem MLog: Acessado pelo menu Log do Sistema
ApplicationID UserControl: Nome da Aplicação
Tabela 3. Configurações das propriedades aos menus no componente

46 ClubeDelphi - Segurança de Sistemas

Clube87.indb 46 26.07.07 10:31:26


SEGUR ANÇ A

Nota: Uma prática segura é alterar a se- podem ser feitas em um editor, acessado de cadastrar.
nha padrão do banco de dados após a ao dar um duplo clique no UserControl. Certamente você já notou que está com
instalação. acesso a todos os menus do sistema e
Testando a aplicação pensou em uma forma de como blo-
Vamos executar a aplicação pela primei- quear o acesso a determinado menu de
Utilizando o User Control ra vez, onde o componente criará automa- acordo com cada usuário. Lembrando
Arraste para o Data Module o UCDBX- ticamente as tabelas por ele requeridas no que durante as configurações criamos
Conn da paleta UC Connectors que acaba- banco de dados e mostrará uma tela com um menu chamado de Perfis de Usuários,
mos de instalar. Faça a configuração da um usuário e senha padrão para proceder vamos acessar o mesmo e criar um novo
sua propriedade Connection ligando-a ao ao primeiro acesso (Figura 5). perfil, ou melhor, passaremos a criar dois
SQLConnection. Realizado o primeiro acesso ao sistema, perfis, um “ADMINISTRADOR” e outro
Volte para o frmPrincipal e solte os acesse o menu Usuários e cadastre um chamado “USUARIOS”.
componentes mostrados na Tabela 1, da novo usuário. No formulário de cadastro, Criados então os perfis, selecione o
paleta UC Main. criado automaticamente pelo componen- perfil USUARIOS e clique sobre o botão
te (Figura 6), clique em Adicionar e faça o acessos. Apareceu um novo formulário,
Nota: Quando estiver no frmPrincipal, cadastro de seu próprio usuário. mas com nenhuma informação para
não esqueça de adicionar a referên- Forneça todas as informações, exceto definições de acesso. Não se preocupe
cia ao Data Module, através do menu a opção Perfil que ainda está em branco você não cometeu nenhum erro, apenas
File>Use Unit, selecione o UntDMCone- e clique em Gravar, será habilitada uma não foi informado ao componente o que
xao. Não esqueça também de definir opção onde deverá ser informada a senha queremos controlar, nesse caso, serão os
que o Data Module será criado antes para o novo usuário (essa é criptografa- menus do sistema.
que o formulário principal. da). Realizando um teste com o usuário Feche a aplicação e voltando ao Del-
criado, através do menu Arquivo>Logoff, phi, no UserControl em sua propriedade
Em Project>Options arraste o Data Modu- acesse novamente o formulário de login ControlRight>MainMenu selecione o
le para que fique posicionado acima ao e informe usuário e senha que acabou componente com a estrutura de menus
frmPrincipal, isso garante que a conexão (MainMenu1).
com o banco de dados já estará aberta Compile e execute sua aplicação no-
quando o componente realizar uma vamente, utilize para login o usuário
consulta de usuário e senha na base. ADMIN, certamente seu usuário não
terá privilégios de acessos aos menus, e
volte a acessar o menu Perfis de Usuários,
Antes de executar a aplicação, preci- selecione o perfil USUÁRIO e clique no-
samos definir os menus de acesso às vamente em acessos.
funcionalidades do componente. Adi- Agora aparecendo todos os menus que
cione ao frmPrincipal um MainMenu, e configuramos anteriormente no com-
com um duplo clique crie uma estrutura ponente, marque os mesmos de forma
semelhante à Tabela 2 (os valores entre Figura 5. Usuário e senha padrão na primeira execução do sistema semelhante à Figura 7 e confirme.
parênteses representam os nomes dos
itens de menu).

No UserControl, definiremos as liga-


ções das propriedades com cada um dos
menus correspondentes. Configure cada
uma das propriedades de acordo com a
Tabela 3.
Para configurar o menu de efetuar logoff,
adicione ao evento OnClick do Logoff o
código a seguir:

procedure TfrmPrincipal.MLogoffClick(Sender:
TObject);
begin
UserControl1.Logoff;
end;

Antes de compilarmos a aplicação, habi-


lite a propriedade AutoStart do UserCon-
trol para True. Algumas configurações Figura 6. Tela para cadastro de um novo usuário do sistema

Edição 87 - ClubeDelphi 47

Clube87.indb 47 26.07.07 10:31:32


Após, selecione o perfil ADMINISTRA- Acabamos de ver uma forma de contro- envio das informações. Precisamos infor-
DOR, defina acesso a todos os itens do lar quem terá ou não acesso ao sistema, e mar ao componente o e-mail do remetente,
menu e confirme. Acesse o menu Cadastro aos menus que cada usuário terá acesso. o nome do mesmo e as informações do
de Usuário, selecione o usuário ADM, e Você poderá criar inúmeros perfis dife- servidor de saída de e-mail (SMTP).
clique em Alterar. Defina-o como membro rentes, e ainda, além de atribuir um usu- Informe o endereço do servidor, como
do perfil ADMINISTRADOR. ário a um perfil, poderá definir acessos por exemplo: “smtp.terra.com.br” ou
Repita o processo com o usuário que individuais a ele, na tela de cadastros. simplesmente o número IP do mesmo.
você cadastrou, determinando que o Clique em Acessos, onde surgirá uma Informe também um usuário válido e a
mesmo será membro do perfil USUA- lista de definições individuais de acessos senha correspondente.
RIOS. Efetue logoff e dessa vez utilize seu ao mesmo. Simularemos o funcionamento do
usuário para conectar ao sistema. Perceba serviço de Esqueci a Senha, e definir
que seus privilégios de acesso foram Funções de e-mail a mensagem que será enviada para o
limitados, onde os menus continuam apa- Tendo adicionado o MailUserControl e-mail do solicitante, juntamente com
recendo, porém foram desabilitados. e configurado o mesmo ao UserControl, usuário e senha. Acessando a proprie-
você possivelmente já deve ter notado a dade EsqueceuSenha>Mensagem e digite
presença de um link Esqueci a Senha no o seguinte texto:
formulário de login, também nas ações
Solicitação de Senha
de cadastros e alterações de usuários, Seu usuário é: :login
Sua Senha é: :senha
onde ao confirmar surgiu rapidamente
uma tela de Envio de Email. O texto digitado possui dois parâmetros,
Todas essas opções são possíveis e já ou seja, login e senha que na hora da so-
estão automaticamente habilitadas nas licitação pelo usuário serão substituídos
funcionalidades do componente, porém pelas devidas informações. Para testar,
ainda não estão funcionando por não es- execute o programa, digite seu usuário e
tarem devidamente configuradas. Então, clique sobre o link Esqueci a Senha.
mãos à obra. Você receberá em seu e-mail a senha
Acessando as propriedades do MailUser- cadastrada para seu usuário. Você po-
Control, perceba que temos de informar ao derá também alterar a mensagem que
mesmo os dados de uma conta de e-mail será enviada para o e-mail, bem como as
Figura 7. Definindo os acessos ao perfil “USUÁRIOS” que serão utilizados para autenticação e o informações que o e-mail carregará.

Figura 8. Usuários logados no sistema

48 ClubeDelphi - Segurança de Sistemas

Clube87.indb 48 26.07.07 10:31:33


SEGUR ANÇ A

Configure da mesma maneira o texto dos mesmos que estão ativos (Figura 8). no sistema. No log de um sistema podem
para as demais opções disponíveis no ficar armazenadas informações muitos
componente. Salvando os Logs das ações do importantes em casos de bugs ou falhas
usuário de informações.
Timeout do sistema e lista de Já que o assunto é segurança, porque No log, qualquer ação do usuário po-
usuários logados não falar sobre log. É através do log de derá ser armazenada, juntamente com a
Muitos sistemas disponibilizam de um sistema que o administrador poderá data/hora e nome do usuário logado no
recursos que controlam o tempo de acompanhar todas as ações dos usuários momento da gravação do log, além é claro
inatividade dos usuários no sistema.
No User Control, basta realizar uma
configuração no UCIdle, alterando sua
propriedade Timeout para o tempo que
exista a seção para aquele usuário, sem
o mesmo estar trabalhando no sistema
(valor em milissegundos).
Em um exemplo de querermos atribuir
um timeout de 20 minutos, bastaria atri-
buir à propriedade o valor “1200000”,
onde esgotado esse tempo, automatica-
mente o componente mostra a tela de
login para que o usuário possa voltar a
utilizar o sistema.
Se seu sistema está instalado em uma
rede e você permitir acesso simultâneo
de vários usuários, o User Control oferece
uma funcionalidade para acompanhar
todos os usuários conectados.
Já configurado anteriormente, basta
você executar o sistema, e através do
menu Usuários Logados obter a listagem

Edição 87 - ClubeDelphi 49

Clube87.indb 49 26.07.07 10:31:43


da ação que o mesmo executou.
Adicione o seguinte código ao evento
OnLoginSuccess do UserControl. Após,
adicionado o código, execute o sistema e
acesse o menu Log:
UserControl1.Log('Efetuou login no sistema', 0);

Acessando o log gerado pelo sistema,


você já deve ter notado a presença de um
registro que você fez login. Em qualquer
formulário da aplicação você poderia
acessar o componente e registrar o log,
como por exemplo, em um formulário de
cadastro, poderíamos criar um log quando
o usuário insere um novo registro, altera,
exclui, entre outras várias opções.
Um log pode ser armazenado com níveis
diferentes de importância para o sistema,
poderá ser armazenado como nível Baixo, 9

de pouca importância para o administra-


dor, como por exemplo, login e logoff. 1
Figura 9. Tela de log do sistema
Também como Normal, a exemplo de
um usuário ter consultado algum relató- 1

rio. Nível Alto, que geralmente é utilizado meras configurações para customização do que as senhas ficam criptografadas no 1

em alterações de registros e por final, um do User Control. Através desse compo- banco de dados.
Crítico, correspondente a uma ação que nente é possível alterar todos os Captions, Vimos também uma maneira fácil e se- 1

o usuário fez e que poderá estar compro- mensagens etc. Acessando as proprie- gura de monitorar através do log tudo que
metendo informações do sistema, como dades mudaremos a posição de todos os acontece no sistema, tudo isso através de 1

por exemplo, a exclusão de um registro de formulários do componente e também o um processo de desenvolvimento muito 1

estoque ou movimentações de caixa. Caption do login. ágil, seguro e com a utilização de poucas
Esses níveis são definidos no momento Alterando a propriedade WindowsPosi- linhas de código. 1
da gravação do log, exemplo do código tion para poScreenCenter definimos que
a seguir, onde informamos a ação que todos os formulários aparecerão ao centro
desejamos gravar e o nível da mesma: da tela do computador. Para alterar o Cap- 2

tion do login, acessaremos a propriedade


UserControl1.Log('Efetuou login no sistema', 0);
{ Nível Baixo (0); Nível Normal (1); Nível Login>WindowsCaption alterando-a para
Alto (2);
Nível Crítico (3) }
“User Control - Tela de Login”. Execute
novamente sua aplicação, veja a mudança 9

A tela de log permite ao usuário rea- (Figura 10). Figura 10. Formulário de login com mudança no Caption
lizar várias opções de filtro, como por 1

exemplo, consultar o log de um usuário Conclusão


específico, em uma determinada data, ou Conforme comentado no início do Links 1

mesmo, pelo nível do log (Figura 9). artigo, vimos uma forma de evitar que 1
Algumas informações foram obtidas do próprio
pessoas indesejadas tenham acesso aos site do componente
Outras configurações seus sistemas, limitando também as prio- www.usercontrol.net
1
No UCSettings, estão disponíveis inú- ridades de acesso aos usuários, lembran-

50 ClubeDelphi - Segurança de Sistemas

Clube87.indb 50 26.07.07 10:31:53


Últimos Ingressos!
31 de Agosto e 01 de Setembro
em São Paulo / SP
O Evento do desenvolvedor
Web e Wireless
Palestras 100% técnicas!
Garanta sua participação e conheça o que as
tecnologias Java e .net trazem de
melhor para este mercado!
Para mais informações acesse:
www.devmedia.com.br/eventos/webmobiletechweek
Realização:
Confira abaixo a Grade de Palestras:

Salas de Java Salas de .NET


1º dia - 31 de Agosto 1º dia - 31 de Agosto
Sala 1 Sala 2 Sala 3 Sala 1 Sala 2 Sala 3
9:00-10:20 Interfaces ricas na Web com Padrões de Projeto Java EE - WEB 2.0: Conceitos e Desenvolvimento para Linq - veja na prática os Ciclo de vida de desenvolvimento
Ajax - Parte 1 Parte 1 Tecnologias dispositivos móveis na fundamentos deste projeto com VSTS
plataforma .NET 2.0 - Parte 1

10:30-11:50 Interfaces ricas na Web com Padrões de Projeto Java EE - Mercado de Jogos para Celulares Desenvolvimento para Mini-curso de C# ASP.NET Caching
Ajax - Parte 2 Parte 2 dispositivos móveis na
plataforma .NET 2.0 - Parte 2

12:00-13:20 Almoço Almoço Almoço Almoço Almoço Almoço

13:30-14-50 Mini Curso 1 - Java ME Struts2 - A Evolução do Utilizando SVG (Scalable Vector Introdução ao ASP.NET AJAX Estratégia de Soluções para XAML
Primeiros Passos - Parte 1 framework - Parte Graphics) com Java ME Mobilidade & Casos de Sucesso

15:00-16:20 Mini Curso 1 - Java ME Struts2 - Desenvolvendo uma Otimização de Aplicações Java ME Aprenda na prática a criar um ASP.NET 2.0 na prática: Profiles e
Primeiros Passos - Parte 2 aplicação com ajax e validação - sistema de enquetes em Acionamento de dispositivos Membership
Parte 1 ASP.NET - Parte 1 eletro-mecanicos com Pocket
PC
16:20-17:00 BREAK BREAK BREAK BREAK BREAK
BREAK
17:00-18:20 Além do básico: explorando Struts2 - Desenvolvendo uma iReport/JasperReport - Criando Aprenda na prática a criar um Criação de um portal com ASP.NET
APIs sofisticadas com Java ME aplicação com ajax e validação relatórios para web sistema de enquetes em Novidades no Windows Mobile 2.0 e WebParts
nas plataformas Nokia - Parte1 Parte 2 ASP.NET - Parte 2 6

18:30-19:50 Além do básico: explorando JavaFX: interfaces em Java de Google Web Toolkit passo-a-passo Desenvolvendo programa para Desenvolvendo Gadgets
APIs sofisticadas com Java ME forma fácil e portável - Parte 1 - Parte 1 PocketPC com rede sem fio e Introdução ao Silverlight Corporativos para Windows Vista
nas plataformas Nokia - Parte2 WebServices - Parte 1

JavaFX: interfaces em Java de Google Web Toolkit passo-a-passo Desenvolvendo programa para .net 3.0 - Xaml Browser
20:00-21:20 Interfaces ricas para a Web forma fácil e portável - Parte 2 - Parte 2 PocketPC com rede sem fio e Criando aplicações WAP com Applications
com Flex 2.0 e Java WebServices - Parte 2 ASP.NET

2º dia - 01 de Setembro 2º dia - 01 de Setembro


Sala 1 Sala 2 Sala 3 Sala 1 Sala 2 Sala 3
9:00-10:20 Implementando uma loja Desenvolvendo um Game em Técnicas avançadas de stress-testing Aplicações com ASP.NET 2.0 e Otimizando aplicações em Aplicações Web para dispositivos
virtual em Java EE 5 com JPA Java ME - Parte 1 de aplicativos Web AJAX - Parte 1 Windows Mobile 5.0 móveis
e EJB3 - Parte 1
Construindo uma aplicação Linq para Compact Framework
10:30-11:50 Implementando uma loja Desenvolvendo um Game em Desenvolvimento produtivo com Java Aplicações com ASP.NET 2.0 e Mobile passo a passo com
virtual em Java EE 5 com JPA Java ME - Parte 2 Enterprise: JSF, JPA, AJAX e EJBs AJAX - Parte 2 Visual Studio 2005
e EJB3 - Parte 2

12:00-13:20 Almoço Almoço Almoço Almoço Almoço Almoço

13:30-14-50 Implementando uma loja Desenvolvendo um Game em Java Wireless: O que nós precisamos Construa uma aplicação 100% Envio e recebimento de dados Silverlight na prática
virtual em Java EE 5 Java ME - Parte 3 saber sobre MIDP3? OO com ASP.NET - Parte 1 no Pocket usando FTP
utilizando JSF para a interface
com o usuário.- Parte 1

15:00-16:20 Implementando uma loja Introdução ao uso do bluetooth Mini Curso 2 – Conectividade em Java Construa uma aplicação 100% Windows Mobile no Windows WorkFlow Foundation no ASP.NET
virtual em Java EE 5 em J2ME ME - Do básico ao avançado - Parte 1 OO com ASP.NET - Parte 2 CE
utilizando JSF para a interface
com o usuário.- Parte 2

16:20-17:00 BREAK BREAK BREAK BREAK BREAK BREAK

17:00-18:20 Desenvolvendo e implantando Desenvolvendo aplicações Mini Curso 2 – Conectividade em Java AJAX Control Toolkit SQL Server 2005 Compact Desenvolvimento WEB: Uma
uma força de vendas em Palm Web com o Spring Framework ME - Do básico ao avançado - Parte 2 Edition - Parte 1 Abordagem Utilizando OO e
- Parte 1 – Parte 1 Técnicas de Teste de Software

18:30-19:50 Desenvolvendo e implantando Desenvolvendo aplicações Multimídia em J2ME RenatoFaria e Net Framework 3.0 - Conheça SQL Server 2005 Compact Testes automatizados em
uma força de vendas em Palm Web com o Spring Framework eduardo peixoto o WPF e surpreenda-se Edition - Parte 2 aplicações ASP.NET
- Parte 2 – Parte 2

Patrocínio: Apoio Cultural: Apoio:

Você também pode gostar