Você está na página 1de 134

Classes - Introduo

Esta srie de artigos tem o objetivo de apresentar o uso de classes e disseminar a utilizao da orientao a objetos dentro de sistemas desenvolvidos em Access / Visual Basic for Applications. A programao orientada a objetos vem se difundindo com grande vulto no mundo todo, resolvendo problemas e causando revoluo no desenvolvimento de sistemas. Ganho de produtividade e qualidade nos produtos so apenas algumas das principais caractersticas deste novo paradigma. Logo no seria muito inteligente da nossa parte, amantes do Access/VBA, nos mantermos distantes desta onda de tecnologia apenas por desconhecimento da ferramenta. Para que todos possam tirar proveito da OO (orientao a objetos) foi que nasceu a idia de ensinar e demonstrar como criar programas com a utilizao de classes no Visual Basic for Applications, a linguagem fornecida pelo MS-Access. O trabalho no ser dirigido para uma verso especfica do Access, mas ser direcionado a todas as verses existentes, atravs da apresentao de conceitos e ferramentas genricos, para que todos tenham condies de se aproveitarem dos conhecimentos aqui transmitidos. No sero descritos nem implementados os padres de projetos que so padres de fato, amplamente utilizados em linguagens especficas e voltadas POO, como Java e PHP, por exemplo. Ao contrrio, ser utilizado um padro prprio para fins didticos, tambm direcionados para a utilizao da ferramenta case Genesis, que ser tema de um dos artigos. Ao final dos artigos teremos um exemplo prtico, pronto para utilizao e visualizao da metodologia empregada. O exemplo ser um banco de dados com um pequeno sistema de vendas feito em Access com as funes totalmente implementadas utilizando classes e objetos. Alm do objetivo principal dos artigos tambm ser apresentada a utilizao de uma ferramenta case, o Genesis, um sotware capaz de auxiliar na construo de um banco de dados funcional, desde a sua documentao at a criao das classes que sero utilizadas pelo sistema. O trabalho contar com 10 artigos, assim divididos: I - ORIENTAO A OBJETOS: Histrico dos paradigmas de linguagens de programao e apresentao de conceitos relativos programao orientada a objetos; II PROGRAMAO OO NO ACCESS/VBA: Apresentao dos recursos de
1

orientao a objetos presentes no Visual Basic for Applications, com a forma de utilizao e as restries existentes; III MODELAGEM DO SISTEMA DE VENDAS: Definio e modelagem de um pequeno sistema de vendas de alimentos, bastantes simples, com o desenho das classes e suas ligaes para posterior implementao; IV AS CLASSES AUXILIARES: Implementao das classes auxiliares, as quais contm funes que sero necessrias para o bom funcionamento das classes principais; V A CLASSE CLIENTE: Definidos os itens da classe ser o momento da implementao da classe Cliente, com a codificao de todos os atributos e mtodos necessrios, alm da interface grfica para manipulao dos dados; VI A CLASSE PRODUTO: Implementao dos atributos e mtodos da classe Produto, alm da interface grfica para manipulao dos dados; VII AS CLASSES VENDA E DETALHE DE VENDA: Implementao dos atributos e mtodos da classe Venda e de sua classe de ligao DetalheVenda, que ser responsvel pelo controle de quais produtos fazem parte de uma venda, alm da interface grfica para manipulao dos dados; VIII FINALIZAO DO SISTEMA: Finalizao do sistema de vendas, com a criao do formulrio principal e dos relatrios para apresentao dos dados; IX GENESIS: A FERRAMENTA CASE: Apresentao do Genesis, um aplicativo capaz de agilizar a criao de um software em Access/VBA que utiliza classes em sua estrutura, alm de produzir elementos para a documentao do sistema. X CONCLUSO: Ao final concluiremos a srie de artigos apresentando alguns apanhados sobre o uso das classes nas diferentes verses do Access e as possibilidades de utilizao da orientao a objetos para melhorar o desempenho dos sistemas desenvolvidos. Vamos ento dar incio a esta jornada de programao imergindo no mundo dos objetos e do nosso querido Access.

Classes II - Programao OO no Access/Vba


O Visual Basic for Applications oferece suporte a apenas alguns dos recursos da orientao a objeto. Ele permite a criao de objetos personalizados com encapsulamento, mtodos construtores e destrutores, e, inclusive, interface de classe. Entretanto no possvel implementar a herana, uma das caractersticas mais importantes da orientao a objetos. Como conseqncia, tambm no possvel utilizar o polimorfismo real com reimplementao de mtodos. O polimorfismo neste caso se restringe a permitir que diferentes classes possam ter o mesmo nome de mtodo, porm com objetivos e funcionamentos diferentes.

Como j vimos o VBA no fornece suporte ao recurso da herana, ento qualquer sistema que envolva classes deve ser projetado utilizando-se cada classe implementada inteiramente. Mesmo que possua caractersticas semelhantes a outras, a utilizao dos mecanismos de generalizao/especializao no poder ser aplicada. Outra caracterstica marcante da metodologia de objetos, que implementada na maioria das linguagens a ela direcionadas, a sobrecarga de mtodos, em que uma mesma classe possui dois ou mais mtodos com o mesmo nome. Neste caso a classe diferencia os mtodos pelo tipo e quantidade de parmetros passados como argumento na realizao da chamada. Infelizmente esta outra funcionalidade que o VBA no suporta. Cada mtodo deve ter o seu nome, diferente de todos os outros dentro da classe. Contudo o objetivo deste artigo apresentar e demonstrar como criar e utilizar uma classe simples no VBA. Por isso mesmo no sero explanados tpicos aprofundados sobre o assunto. Caso voc se interesse em conhecer um pouco mais sobre a criao de objetos pode consultar a prpria ajuda do VBA, que possui definies, explicaes e exemplos sobre todos os assuntos aqui abordados. Criando uma Classe no VBA Para criar uma classe no VBA basta abrir o editor, atravs do menu do Access ou utilizando a combinao de teclas Alt+F11. No editor escolhemos a opo Mdulo de classe no menu Inserir da janela do editor, conforme figura abaixo:

No momento em que salvarmos a classe o nome escolhido ser o nome que utilizaremos para criar objetos desta classe. A classe do exemplo foi salva com o nome clsCliente. Diferena Entre os Tipos de Mdulos No VBA podemos ter os seguintes tipos de mdulos: Mdulo Padro, Mdulo de Formulrio/Relatrio e Mdulo de Classe. A diferena bsica entre estes tipos de mdulos est na visibilidade das informaes de cada um. Em um mdulo padro, antes chamado de mdulo de cdigo, podemos colocar constantes, variveis, procedimentos e funes que so diretamente acessveis de qualquer lugar do nosso projeto, dependendo apenas dos modificadores de acesso utilizados. J em um mdulo de classe no possvel acessar os atributos e mtodos sem que o objeto seja instanciado. Resumindo ns podemos chamar uma funo de um mdulo padro sem que seja preciso primeiro criar uma varivel de objeto para o mdulo em questo. Para funes dentro de um mdulo de classe isto no possvel. J o mdulo de formulrio/relatrio trata-se apenas de um mdulo de classe que pertence ao respectivo formulrio/relatrio, ou seja, contm os atributos e mtodos relativos ao formulrio/relatrio em questo. Os mdulos padro e de classe devem ser explicitamente criados. J os mdulos de formulrio/relatrio so criados automaticamente quando criamos um formulrio ou relatrio.

Ento resta decidir sobre quando utilizar um mdulo padro ou um mdulo de classe. Voc tem ou j teve esta dvida? A resposta est no fato de suas variveis e seus procedimentos/funes serem especficos ou genricos. Caso possam ser usados por qualquer entidade, em qualquer mdulo, a qualquer momento seria bom que estivessem em um mdulo padro para permitir o acesso direto e imediato. Um exemplo deste caso seria uma funo que converte um valor numrico em valor por extenso. Com certeza esta uma funo bem genrica. J em se tratando de algo especfico, que s tenha a ver com aquele determinado objeto, por exemplo um clculo de estoque de uma venda, deveria ser implementado dentro da classe que modela os objetos de venda, para que fosse acessado somente durante a existncia da mesma. Os defensores mais radicais da POO diriam que mesmo para funes genricas deveria ser criada uma classe para agrupar aquelas que fossem semelhantes. Criando os Atributos da Classe Antes de criarmos os atributos vamos incluir algumas diretivas dentro no nosso mdulo de classe para restringir a ocorrncia de erros e definir o padro de comparao de dados no sistema. Para isto utilizamos as seguintes opes: > Para definir o mtodo padro de comparao de dados dentro do mdulo de classe:
Option Compare Database

> Para obrigar a declarao de variveis dentro do mdulo de classe:


Option Explicit

Obs: Estas opes no so obrigatrias na criao das classes, mas uma boa prtica adot-las em seus mdulos. A opo de comparao deve ser aquela mais adequada ao tipo de comparao desejada. Para maiores informaes sobre estas duas opes, alm de outras, consulte a documentao do VBA. Agora para criarmos atributos dentro da nossa classe basta declararmos as variveis ou constantes, lembrando que no caso destas no poderemos atribuir valores. Como iremos declarar as variveis diretamente no mdulo, sem que estejam subordinadas a um procedimento ou funo, no poderemos fazer esta declarao usando a instruo Dim. Neste caso deveremos utilizar os modificadores de acesso a seguir: Private: para atributos privados, ou seja, aqueles que no so visveis fora do mdulo. Normalmente utilizamos este modificador e configuramos o acesso aos atributos atravs dos mtodos de atribuio e retorno de valores que sero vistos a seguir; Public: para atributos pblicos, ou seja, aqueles que so visveis fora do mdulo. Conforme citado anteriormente, uma regra da POO que os atributos no sejam diretamente acessveis fora da classe. Isto o encapsulamento das informaes.

Vamos criar um atributo pblico em nossa classe para que possamos visualizar a sua utilizao:
Diretivas iniciais Option Compare Database Option Explicit Primeiro atributo: nome do cliente Public nomeCliente As String

Salvamos a classe e agora vamos criar um mdulo padro para testar a nossa classe criada com seu primeiro e nico atributo pblico. Crie um mdulo padro, salve-o com o nome de TesteClasse e inclua este cdigo em um procedimento chamado Teste():

{geshi lang="asp" lines="true" tit="Testando o Objeto"}Option Compare Database Option Explicit Sub Teste() 'Declarando o objeto cliente Dim objCliente As New clsCliente 'Atribuindo um valor ao atributo nomeCliente objCliente.nomeCliente = "Plinio Mabesi" 'Exibindo o valor do atributo MsgBox "Nome do Cliente: " & objCliente.nomeCliente End Sub{/geshi} Perceba que assim que digitamos o ponto aps o nome do objeto o VBA nos mostra quais atributos e mtodos esto disponveis, para que possamos escolher um deles na lista suspensa, conforme a figura abaixo:

Aps o procedimento estar completo posicione o cursor dentro dele e pressione F5 para executar. Veja o resultado apresentado na caixa de mensagem. Faa novos testes alterando o nome do cliente e reveja o resultado. Atributos de Classe O VBA no fornece o recurso presente em outras linguagens em que utilizamos o modificador Static para criar o chamado atributo de classe, o qual no depende da instanciao do objeto para ser acessado. Ele um atributo que no est ligado a nenhum objeto, ou seja, ele existe sem que seja necessrio declarar uma varivel de objeto da classe. No VBA a instruo Static tem outra funo: a de reter um valor de uma varivel dentro de um procedimento ou funo entre as diversas chamadas. Caso no se use esta instruo o valor de uma varivel perdido assim que o procedimento/funo for encerrado. Com a instruo Static este valor estar disponvel na prxima chamada, desde que o aplicativo ainda esteja sendo executado. Sendo assim todos os atributos e mtodos de uma classe s estaro disponveis depois que o objeto for instanciado. Mtodos de Acesso aos Atributos Conforme prev as regras da POO, no se deve fornecer acesso direto aos atributos de um objeto, obedecendo ao conceito de encapsulamento, pois no caso de qualquer alterao no tipo de dado ou incluso de uma ao a ser executada quando da atribuio ou recuperao de um valor do atributo, a classe continuar oferecendo a mesma interface de comunicao com outras classes. Isto resulta em esforo mnimo para a manuteno ou extenso do cdigo. O VBA fornece um recurso nativo e prprio para a implementao de mtodos que atribuem ou retornam valores de atributos do objeto, os conhecidos getters e setters.
7

So os procedimentos Property Let (atribuem valores aos atributos, equivalente ao set em outras linguagens) e Property Get (retornam valores de atributos, equivalente ao get das outras linguagens). Ambos os procedimentos devem ter o mesmo nome, mas ao instanciar um objeto o usurio tem acesso aos mtodos como se fossem uma nica propriedade. Para atribuir um valor basta igualar a propriedade ao valor de uma varivel, por exemplo. Da mesma forma, para recuperar o valor de um atributo para igualar uma varivel propriedade. Vamos alterar nossa classe para deixar o atributo protegido utilizando os procedimentos Property Get e Property Let. O cdigo da classe agora ficar desta maneira:
Option Compare Database Option Explicit 'Protegendo o atributo que ficar 'visvel apenas dentro da classe Private strNomeCliente As String Property Get nomeCliente() As String 'Retornando o valor do atributo nomeCliente = strNomeCliente End Property Property Let nomeCliente(argNomeCliente As String) 'Atribuindo valor ao atributo atravs 'do argumento passado para o mtodo strNomeCliente = argNomeCliente End Property

Perceba que alteramos o nome da varivel para strNomeCliente j que os mtodos se chamaro nomeCliente. Isto deve ser feito pois o VBA no aceita que o mtodo tenha o mesmo nome do atributo. Ao digitarmos o nome do objeto em um mdulo qualquer veremos a lista suspensa da mesma maneira que vimos anteriormente, com apenas uma opo de escolha, porm desta vez quem ser acessado um dos procedimentos Property (Get ou Let) que depender da ao a ser executada, sendo uma atribuio ou um retorno. Existe tambm outro mecanismo, diferente de outras linguagens, utilizado para referncia a objetos. Quando um objeto possui um atributo que faz referncia a outro objeto, a atribuio feita atravs do procedimento Property Set e no atravs do procedimento Property Let. Para retornar o objeto referenciado utiliza-se tambm o procedimento Property Get, porm ainda faz-se necessria a utilizao da instruo Set por se tratar de um objeto.
'Declarando um atributo do tipo objeto Private objAtributoObjeto As Object Property Get nomeDoAtributo() As Object

'Retornando o objeto Set nomeDoAtributo = objAtributoObjeto End Property Property Set nomeDoAtributo(argAtributo As Object) 'Atribuindo um objeto ao atributo Set objAtributoObjeto = argAtributo End Property

Executando Cdigo ao Atribuir ou Retornar Valores dos Atributos Uma das maiores vantagens em se utilizar mtodos para atribuir ou retornar valores est na capacidade de executar cdigos imediatamente antes ou aps a chamada ao atributo. Suponhamos que o nome do cliente possa ser informado em letras minsculas ou maisculas, porm desejamos que ele seja armazenado somente em maisculas. Neste caso inclumos um cdigo que faz o ajuste antes de realmente atribuir o valor:
Property Let nomeCliente(argNomeCliente As String) 'Passando o nome do cliente para maiscula 'antes de efetivamente atribu-lo strNomeCliente = UCase(argNomeCliente) End Property

Assim podemos fazer qualquer validao ou ajuste necessrio antes de repassar um valor ao atributo. Entretanto em outra ocasio poderamos desejar que o nome do cliente pudesse ser informado em letras minsculas ou maisculas, e que ele tambm possa ser armazenado desta maneira. Porm desejamos que ao retornar o valor ele seja convertido para maisculas apenas para ser apresentado. Neste caso inclumos um cdigo que faz o ajuste no momento de retornar o valor do atributo:
Property Get nomeCliente() As String 'Passando o nome do cliente para maiscula 'no momento de retorn-lo ao chamador nomeCliente = UCase(strNomeCliente) End Property

Criando os Mtodos da Classe Para criarmos mtodos basta usarmos as instrues Sub ou Function, comuns aos outros tipos de mdulos, precedidos ou no dos modificadores de acesso para visibilidade, e os mesmos formatos de declarao, com tipos de retorno, parmetros e regras de nomenclatura.

Observao: Mais uma vez volto a frisar que ensinar regras de criao de procedimentos e funes no est no escopo deste trabalho, tampouco a construo de algoritmos, ento aos interessados cabe consultar os manuais da documentao do VBA ou as milhares de apostilas existentes na Internet. Ou quem sabe em outro artigo... Como do conhecimento de todos utilizamos a instruo Sub para declarar procedimentos, ou seja, cdigos que ao final de sua execuo no retornam nenhum valor. J a instruo Function serve para declararmos funes, ou seja, cdigos que ao final devem retornar algum resultado para o chamador. Procedimentos e funes declaradas so pblicas por padro, a no ser que se indique explicitamente o modificador de acesso. Para este caso, alm de Private e Public podemos ainda usar o modificador Friend, o qual ajusta a visibilidade do mtodo para ser acessvel apenas dentro do projeto atual. Como exemplo vamos criar dois mtodos em nossa classe, um que no retorna nenhum valor e servir para exibir os dados do cliente em uma caixa de mensagem, e outro que dever retornar a idade do cliente com base na sua data de nascimento. Claro que para isto deveremos incluir o atributo data de nascimento, concorda? Ento aps incluir todos os cdigos em nossa classe ela estar pronta e dever ficar assim:
Option Compare Database Option Explicit 'Atributos=========================================== 'Nome do Cliente Private strNomeCliente As String 'Data de Nascimento do Cliente Private dtmDataNascimento As Date 'Mtodos Property Get / Let / Set =================== Property Get nomeCliente() As String nomeCliente = strNomeCliente End Property Property Let nomeCliente(argNomeCliente As String) strNomeCliente = UCase(argNomeCliente) End Property Property Get dataNascimento() As Date dataNascimento = dtmDataNascimento End Property Property Let dataNascimento(argDataNascimento As Date) dtmDataNascimento = argDataNascimento End Property 'Mtodos============================================

10

Sub mostraDadosCliente() 'Mtodo que mostra o nome do 'cliente em uma caixa de mensagem MsgBox "Nome do Cliente: " & nomeCliente & vbCrLf & _ "Data de Nascimento: " & dataNascimento & vbCrLf & _ "Idade: " & calculaIdade, vbInformation, "Dados do Cliente" End Sub Function calculaIdade() As Integer 'Mtodo que calcula a idade com 'base na data de nascimento Dim Dim Dim Dim anoAtual As Integer anoNascimento As Integer totalAnos As Integer aniversario As String

aniversario = Format(dataNascimento, "dd/mm") anoNascimento = Year(dataNascimento) anoAtual = Year(Date) totalAnos = anoAtual - anoNascimento If CDate(aniversario & "/" & anoAtual) <= Date Then calculaIdade = totalAnos Else calculaIdade = totalAnos - 1 End If End Function

Perceba que ao chamarmos o mtodo calculaIdade() no mtodo mostraDadosCliente() fazemos a chamada diretamente. Isto acontece porque os dois mtodos esto dentro da mesma classe. Qualquer chamada de fora da classe somente ser possvel depois da declarao da varivel de objeto da classe, ou seja, depois que o objeto for instanciado. Retornando ao nosso mdulo de teste podemos observar agora que temos mais opes na lista suspensa ao digitar o ponto aps o nome do objeto:

11

Vamos ento atribuir valores s propriedades e testar os novos mtodos do objeto a partir do procedimento Teste() em nosso mdulo TesteClasse:
Option Compare Database Option Explicit Sub Teste() 'Declarando o objeto cliente Dim objCliente As New clsCliente 'Atribuindo um valor ao atributo nomeCliente objCliente.nomeCliente = "Plinio Mabesi" 'Atribuindo um valor ao atributo dataNascimento objCliente.dataNascimento = "16/01/1976" 'Exibindo a idade do cliente em uma caixa de mensagem MsgBox "Idade do cliente: " & objCliente.calculaIdade 'Exibindo todos os dados do cliente, mas agora 'utilizando o mtodo prprio criado para isto objCliente.mostraDadosCliente End Sub

Voc poder executar o cdigo inteiro posicionando o cursor dentro do procedimento com a tecla F5 ou ento passo a passo usando a tecla F8, podendo inclusive acompanhar a atribuio de cada um dos valores aos atributos. Trabalhando com Vrios Objetos Outro aspecto interessante de se trabalhar com objetos que a partir de um nico mdulo podemos criar quantos objetos forem necessrios, e o que melhor, eles podem

12

interagir e coexistir simultaneamente. Para isto basta declararmos cada objeto com um nome diferente. Vamos a um exemplo prtico em nosso mdulo de testes, no qual dois objetos cliente tero as suas idades comparadas para saber quem tem mais idade, sem considerar meses e dias:
Sub TesteIdade() 'Declarando os objetos cliente Dim objCliente01 As New clsCliente Dim objCliente02 As New clsCliente 'Atribuindo valores aos objetos objCliente01.nomeCliente = "Plinio Mabesi" objCliente01.dataNascimento = "16/01/1976" objCliente02.nomeCliente = "Avelino Sampaio" objCliente02.dataNascimento = "20/08/1965" 'Realizando a comparao entre os dois objetos If objCliente01.calculaIdade = objCliente02.calculaIdade Then MsgBox objCliente01.nomeCliente & " e " & objCliente02.nomeCliente & " tem a mesma idade." ElseIf objCliente01.calculaIdade < objCliente02.calculaIdade Then MsgBox objCliente01.nomeCliente & " mais novo que " & objCliente02.nomeCliente & "." Else MsgBox objCliente02.nomeCliente & " mais novo que " & objCliente01.nomeCliente & "." End If End Sub

Execute e veja o resultado. Altere os valores e analise o comportamento e o resultado das variaes. Tente inventar outros testes, inclua novos atributos na classe, assim como novos mtodos, use a imaginao, treine, treine e treine, pois s assim voc comear a dominar a tcnica da programao orientada a objetos. Voc no vai querer continuar fora desta, vai? Criando uma Interface de Classe H situaes em que interessante que algumas classes implementem certos mtodos que so padro para todas elas. Neste caso podemos criar uma interface, que nada mais do que uma classe que possui mtodos no implementados, que devero ser construdos na classe que est utilizando a interface. Por isso ela serve apenas como um contrato de implementao, o que significa que a classe dever obrigatoriamente possuir aqueles mtodos com os mesmos parmetros de entrada e o mesmo tipo de dado de

13

retorno. Mas a classe no precisa possuir somente os mtodos da interface, ela poder ter quantos mtodos forem necessrios, desde que implemente os obrigatrios. Diferentemente de outras linguagens, no VBA uma classe totalmente implementada pode ser interface de outras, bastando para isso incluir a instruo Implements dentro da classe que deseja implementar a interface de outra. Como exemplo se quisssemos criar outra classe em nosso pequeno projeto, agora para fornecedores, mas que obrigatoriamente tivesse que implementar todos os mtodos da nossa classe de clientes deveramos incluir este cdigo dentro do mdulo da classe clsFornecedor, logo no incio:
Implements clsCliente

Quando inserimos a instruo Implements em uma classe o menu dropdown no editor de cdigo, logo abaixo da barra de ferramentas nos apresenta as opes disponveis, bastando selecion-las para que sejam includas na classe atual. Veja a figura:

Os mtodos implementados desta maneira devero conter o nome da interface utilizada antes do nome do mtodo. Assim nossa classe clsFornecedor ficaria com o seguinte aspecto, com os mtodos ainda a serem implementados:
Option Compare Database Option Explicit Implements clsCliente Private Property Get clsCliente_dataNascimento() As Date End Property Private Property Let clsCliente_dataNascimento(RHS As Date) End Property Private Property Let clsCliente_nomeCliente(RHS As String)

14

End Property Private Property Get clsCliente_nomeCliente() As String End Property Private Sub clsCliente_mostraDadosCliente() End Sub Private Function clsCliente_calculaIdade() As Integer End Function

lgico e evidente que este apenas um exemplo utilizando uma classe j pronta, e que no serviria em uma situao real pois os nomes dos mtodos foram criados para um cliente e no para um fornecedor. Na verdade deveramos criar uma interface com nomes de mtodos genricos que pudessem servir para as classes de modo geral. O mtodo mostraDadosCliente() poderia se chamar apenas mostraDados(). J o mtodo calculaIdade() est em um padro aceitvel e poderia ser utilizado para qualquer classe que se tratasse de uma pessoa. Note que o prprio VBA modifica os nomes dos parmetros de mtodos que os exigem. Porm isto no obrigatrio e voc pode alterar novamente estes argumentos. Concluso Aqui se encerra mais esta etapa da nossa srie. Neste artigo aprendemos as tcnicas bsicas para se criar uma classe no VBA, criar e proteger seus atributos, alm de como criar e utilizar os mtodos da classe para oferecer funcionalidade. Vimos tambm como trabalhar com mais de um objeto da classe ao mesmo tempo, assim como tivemos uma noo sobre implementao de interfaces. Com isso percorremos mais um trecho na caminhada rumo ao conhecimento do paradigma orientado a objetos.

15

Classes III - Modelagem do Sistema de Vendas


Alm disso o modelo adotado tem carter didtico e no pretende servir como um padro para aplicaes reais. As relaes criadas, as funes definidas e os atributos escolhidos tero como objetivo maior demonstrar como funciona a aplicao da orientao a objetos no Access / VBA, mesmo no sendo a forma mais correta e eficiente de modelagem de classes ou de banco de dados. Portanto preocupe-se em visualizar e compreender bem as tcnicas utilizadas nas chamadas de mtodos, criao de atributos, passagem de parmetros, enfim esteja focado em aprender como se d a interao entre os objetos dentro do sistema. Padro de Nomenclatura O padro que ser utilizado neste trabalho obedecer s seguintes definies: > No sero usados acentos, smbolos ou espaos nos nomes, j que isto uma prtica que causa problemas na maioria das linguagens de programao existentes, no sendo nem mesmo aceita em vrias delas. > Nomes de classes comeando em maisculas, com o restante em minsculas. Quando o nome da classe for formado por mais de uma palavra elas sero separadas por maiscula. Ex: Cliente, Venda, DetalheVenda; > Nomes de atributos em minsculas. Quando o nome do atributo for formado por mais de uma palavra elas sero separadas por maiscula. Ex: cpf, nomeCliente, descricao; > Nomes de mtodos em minsculas seguidos por parnteses. Quando o nome do atributo for formado por mais de uma palavra elas sero separadas por maiscula. Ex: obter(), salvar(), consulta(), calculaClasse(); > Nomes de tabelas comeando em maisculas, com o restante em minsculas. Quando o nome da tabela for formado por mais de uma palavra elas sero separadas por maiscula. Ex: Cliente, Venda, DetalheVenda; > Nomes de campos de tabelas em minsculas. Quando o nome do campo for formado por mais de uma palavra elas sero separadas por maiscula. Ex: cpf, nomeCliente, descricao; > Nomes de consultas comeando em maisculas, precedidos pela letra C, com o restante em minsculas. Quando o nome da consulta for formado por mais de uma palavra elas sero separadas por maiscula.
16

Ex: CCliente, CVenda, CDetalheVenda; > Nomes de formulrios comeando em maisculas, precedidos pela letra F, com o restante em minsculas. Quando o nome do formulrio for formado por mais de uma palavra elas sero separadas por maiscula. Ex: FCliente, FVenda, FDetalheVenda; > Nomes de relatrios comeando em maisculas, precedidos pela letra R, com o restante em minsculas. Quando o nome o relatrio for formado por mais de uma palavra elas sero separadas por maiscula. Ex: RCliente, RVenda, RDetalheVenda; Obs: No momento da implementao das classes ser adicionado o prefixo cls ao nome daquelas que dizem respeito a objetos persistentes e acl ao nome das que so auxiliares. O motivo desta escolha ser explicado no artigo sobre o Genesis, a ferramenta case para o Access. Modelo de Classes Para criarmos um sistema de vendas necessitaremos dos seguintes itens bsicos: 1- Os PRODUTOS que sero vendidos; 2- Os CLIENTES que iro comprar os PRODUTOS; 3- Cada grupo de PRODUTOS vendido a um CLIENTE ir gerar uma VENDA. Ento inicialmente podemos dizer que nosso sistema dever ser capaz de manipular os objetos Cliente, Produto e Venda. Sendo assim estas sero as classes que deveremos modelar. Obs: Consideraremos que j foi feito um prvio levantamento de requisitos e que j foram definidos quais atributos e mtodos as classes devem ter. Nas interaes entre os objetos poderemos perceber que: 1- Um cliente pode ser cadastrado mesmo que no tenha comprado ainda; 2- Ento um cliente cadastrado pode comprar zero ou mais vezes, por isso podero ser realizadas zero ou mais vendas para cada cliente; 3- Cada venda realizada deve ser feita, obrigatoriamente, a um determinado cliente; 4- Cada produto cadastrado pode estar presente ou no em uma ou mais vendas; 5- Cada venda realizada deve conter, obrigatoriamente, um ou mais produtos.

17

A figura abaixo ilustra ento o modelo de classes referente descrio anterior:

Para executar funes genricas e comuns a vrias classes do sistema criaremos ainda mais duas classes, uma para realizar a conexo com banco de dados e persistir ou buscar os objetos e outra para realizar tarefas de validao e transformao de valores, as quais sero utilizadas pela classe Cliente. Estas sero chamadas de ConexaoBD e Utilitario. Mas antes de definirmos o esquema bsico de nossas classes temos que ajustar a relao entre Venda e Produto. Como todos j perceberam esta uma relao muitos-paramuitos, ento as regras de modelagem nos mandam criar uma classe de associao para gerenciar a ligao entre elas. Portanto teremos tambm a classe DetalheVenda. Estas sero as outras classes criadas para completar nosso modelo:

Descrio das Classes A seguir sero descritas as estruturas bsicas de cada uma das classes. Porm neste artigo ainda no sero detalhadas as formas de implementao das funcionalidades de cada mtodo definido no modelo. Apenas a assinatura de cada mtodo ser informada, contendo seu objetivo, seus parmetros de entrada e seu tipo de retorno. Os mtodos Let, Get e Set tambm no sero abordados por enquanto. Classe Utilitrio Objetivo: oferecer funcionalidades de validao de dados e transformao de valores, necessrias para o correto funcionamento da classe Cliente, ou qualquer outra classe ou funo que venha a precisar dela, a qualquer momento. Isto se deve ao fato da classe possuir mtodos genricos e reutilizveis, podendo ser reaproveitados, sem nenhuma alterao, em qualquer sistema. Funes: validaCpf(argCpf As String) As Boolean: recebe como argumento um texto contendo um CPF, sem pontos nem trao, e devolve um valor booleano, verdadeiro ou falso,
18

indicando se o CPF ou no vlido; validaEmail(argEmail As String) As Boolean: recebe como argumento um texto contendo um e-mail e devolve um valor booleano, verdadeiro ou falso, indicando se o email ou no vlido, sendo que a verificao feita apenas no seu formato; nomeProprio(argNome As String) As String: recebe como argumento um texto qualquer e devolve o mesmo texto com as iniciais dos nomes em maisculas e o restante em minsculas, levando em considerao as partculas de ligao de nomes, as quais permanecem em minsculas; desacentua(argTexto As String) As String: recebe como argumento um texto qualquer e devolve o mesmo texto sem acentos; abreviaNome(argNome As String) As String: recebe como argumento um texto qualquer e devolve o mesmo texto com a penltima parte do nome abreviada, levando em considerao as partculas de ligao de nomes, as quais permanecem inalteradas. Classe ConexaoBD Objetivo: oferecer funcionalidades de consulta e atualizao do banco de dados. Possui tambm mtodos genricos que podem ser reaproveitados em qualquer sistema. Uma das maiores vantagens de se utilizar uma classe de conexo que no caso de mudana do tipo de Sistema Gerenciador de Banco de Dados (SGBD) somente ela dever ser alterada, permanecendo todas as outras intocadas, desde que seja mantida a interface de comunicao dos mtodos. Um exemplo seria a troca do back-end para MySql, Postgres ou Sql Server. Funes: executa(codigoSql As String) As Long: recebe como argumento um texto contendo um cdigo SQL de insero, excluso ou alterao, executa o cdigo e devolve o nmero de registros afetados na operao; consulta(codigoSql As String, Optional editavel As Boolean = False) As Recordset: recebe como argumento um texto contendo um cdigo SQL de consulta de registros e um valor lgico que define o modo de bloqueio de execuo, permitindo ou no a edio dos dados durante a operao, e devolve um recordset contendo o conjunto de registros que atenderem aos critrios; logicoSql(ByVal argValor As Boolean) As String: recebe como parmetro um valor booleano e devolve um texto contendo os valores True ou False, necessrio para cdigos SQL; pontoVirgula(varValor As Variant) As String: recebe como parmetro um valor decimal em que o padro de separao da parte inteira e a decimal a vrgula, e devolve um texto contendo o mesmo valor agora separado por ponto, necessrio para cdigos SQL; dataSql(ByVal argData As Date) As String: recebe como parmetro uma data em qualquer formato e devolve um texto contendo a data no padro #mm/dd/yyyy#,
19

necessrio para cdigos SQL; valorSql(ByVal argValor As Variant) As String: recebe como parmetro um valor qualquer, verifica o seu formato e devolve um valor no formato padro dos cdigos SQL, fazendo uso das funes anteriores, caso necessrio. Classe Cliente Objetivo: oferecer funcionalidades de consulta e atualizao dos objetos do tipo cliente. Atributos: codCliente (Long); cpf (String); nomeCliente (String); email (String); renda (Currency); classe (String). Funes: existe(argCodCliente As Long) As Boolean: recebe como argumento um cdigo de um cliente, utiliza a classe de conexo para consultar e devolve um valor booleano que indica se o cliente existe ou no no banco de dados; incluir() As Boolean: persiste o objeto atual no banco de dados, utilizando a classe de conexo, e devolve um valor booleano que indica se a operao ocorreu com sucesso. Este um mtodo privado, somente sendo acessvel pelos mtodos internos da classe; obter(argCodCliente As Long) As Boolean: recebe como argumento um cdigo de um cliente, utiliza a classe de conexo para consultar, atualiza o objeto com os dados e devolve um valor booleano que indica se o cliente foi buscado com sucesso; salvar() As Boolean: salva objeto atual no banco de dados, utilizando a classe de conexo, e devolve um valor booleano que indica se a operao ocorreu com sucesso. Este mtodo atualiza o objeto atual caso ele j exista ou ento o inclui no banco de dados caso seja um novo objeto; excluir() As Boolean: exclui o objeto atual no banco de dados, utilizando a classe de conexo, e devolve um valor booleano que indica se a operao ocorreu com sucesso; calculaClasse() As String: calcula a classe social do objeto atual baseado na renda informada e devolve uma letra correspondente sua classificao dentro das faixas salariais. Classe Produto Objetivo: oferecer funcionalidades de consulta e atualizao dos objetos do tipo produto.

20

Atributos: codProduto (Long); unidade (String); descricao (String); valorUnitario (Currency); estoqueMinimo (Double); qtdEstoque (Double). Funes: existe(argCodProduto As Long) As Boolean: recebe como argumento um cdigo de um produto, utiliza a classe de conexo para consultar e devolve um valor booleano que indica se o produto existe ou no no banco de dados; incluir() As Boolean: persiste o objeto atual no banco de dados, utilizando a classe de conexo, e devolve um valor booleano que indica se a operao ocorreu com sucesso. Este um mtodo privado, somente sendo acessvel pelos mtodos internos da classe; obter(argCodProduto As Long) As Boolean: recebe como argumento um cdigo de um produto, utiliza a classe de conexo para consultar, atualiza o objeto com os dados e devolve um valor booleano que indica se o produto foi buscado com sucesso; salvar() As Boolean: salva objeto atual no banco de dados, utilizando a classe de conexo, e devolve um valor booleano que indica se a operao ocorreu com sucesso. Este mtodo atualiza o objeto atual caso ele j exista ou ento o inclui no banco de dados caso seja um novo objeto; excluir() As Boolean: exclui o objeto atual no banco de dados, utilizando a classe de conexo, e devolve um valor booleano que indica se a operao ocorreu com sucesso; baixarEstoque(argQtd As Double) As Boolean: atualiza o estoque diminuindo a quantidade informada como parmetro e devolve um valor booleano que indica o sucesso da operao; subirEstoque(argQtd As Double) As Boolean: atualiza o estoque acrescentando a quantidade informada como parmetro e devolve um valor booleano que indica o sucesso da operao; estoqueBaixo() As Boolean: verifica o estoque atual e compara com o valor de estoque mnimo cadastrado para produto e devolve um valor booleano que indica se o valor atual est abaixo do previsto. Classe Venda Objetivo: oferecer funcionalidades de consulta e atualizao dos objetos do tipo venda. Atributos:

21

codVenda (Long); codCliente (Long); dataVenda (Date); Funes: existe(argCodVenda As Long) As Boolean: recebe como argumento um cdigo de uma venda, utiliza a classe de conexo para consultar e devolve um valor booleano que indica se o venda existe ou no no banco de dados; incluir() As Boolean: persiste o objeto atual no banco de dados, utilizando a classe de conexo, e devolve um valor booleano que indica se a operao ocorreu com sucesso. Este um mtodo privado, somente sendo acessvel pelos mtodos internos da classe; obter(argCodVenda As Long) As Boolean: recebe como argumento um cdigo de uma venda, utiliza a classe de conexo para consultar, atualiza o objeto com os dados e devolve um valor booleano que indica se o venda foi buscada com sucesso; salvar() As Boolean: salva objeto atual no banco de dados, utilizando a classe de conexo, e devolve um valor booleano que indica se a operao ocorreu com sucesso. Este mtodo atualiza o objeto atual caso ele j exista ou ento o inclui no banco de dados caso seja um novo objeto; excluir() As Boolean: exclui o objeto atual no banco de dados, utilizando a classe de conexo, e devolve um valor booleano que indica se a operao ocorreu com sucesso; getValorTotal() As Currency: Calcula o valor total da venda atual e devolve o valor como resultado. Classe DetalheVenda Objetivo: oferecer funcionalidades de consulta e atualizao dos objetos de ligao entre a venda e os produtos componentes. Atributos: codVenda (Long); codProduto (Long); qtdProduto (Double); Funes: existe(argCodVenda As Long, argCodProduto As Long) As Boolean: recebe como argumento um cdigo de uma venda e de um produto, utiliza a classe de conexo para consultar e devolve um valor booleano que indica se objeto existe ou no no banco de dados; incluir() As Boolean: persiste o objeto atual no banco de dados, utilizando a classe de conexo, e devolve um valor booleano que indica se a operao ocorreu com sucesso. Este um mtodo privado, somente sendo acessvel pelos mtodos internos da classe;
22

obter(argCodVenda As Long, argCodProduto As Long) As Boolean: recebe como argumento um cdigo de uma venda e de um produto, utiliza a classe de conexo para consultar, atualiza o objeto com os dados e devolve um valor booleano que indica se o objeto foi buscado com sucesso; salvar() As Boolean: salva objeto atual no banco de dados, utilizando a classe de conexo, e devolve um valor booleano que indica se a operao ocorreu com sucesso. Este mtodo atualiza o objeto atual caso ele j exista ou ento o inclui no banco de dados caso seja um novo objeto; excluir() As Boolean: exclui o objeto atual no banco de dados, utilizando a classe de conexo, e devolve um valor booleano que indica se a operao ocorreu com sucesso; getSubTotal() As Currency: Calcula o subtotal do produto vendido para a venda atual e devolve o valor como resultado; getListaProduto(argCodVenda As Long) As Recordset: recebe como parmetro um cdigo de uma venda e devolve um conjunto de registros contendo os cdigos dos produtos relativos venda informada. Diagrama de Alto Nvel Organizando as relaes entre todas as classes do sistema chegamos a este modelo de alto nvel, ainda sem informaes de atributos e mtodos, para melhor visualizao do contexto do sistema:

Diagrama de Classes Detalhado Incluindo agora o restante das informaes de atributos e mtodos chegamos a este diagrama detalhado:
23

A fim de evitar sobrecarregar o modelo com informaes foram omitidos os mtodos de acesso aos atributos, os Get / Let / Set, que sero includos apenas na implementao das classes, nos prximos artigos. Alm disso atributos auxiliares tambm no foram descritos nesta etapa, mas sero includos no momento da implementao das classes. Modelo de Dados Para que um objeto seja persistente, ou seja, possa ser recuperado posteriormente, ele dever ser gravado em algum meio permanente. Neste caso vamos persistir nossos objetos em um gerenciador de banco de dados bastante conhecido de todos: o prprio Access. Realizando o mapeamento das classes para o formato de banco dados do Access temos ento o dicionrio de dados, com as informaes sobre cada tabela e seus campos, alm dos relacionamentos entre elas, necessrios para manter a integridade dos dados. Temos tambm o diagrama de entidade-relacionamento, que a apresentao grfica do dicionrio. Ressalto que do nosso DER foram suprimidas as informaes detalhadas dos campos para evitar sobrecarregar o modelo, j que este tambm no o foco do presente trabalho. Dicionrio de Dados No dicionrio de dados de um modelo de dados esto descritas as informaes detalhadas sobre as tabelas, seus campos, tipos de dados, tamanho dos campos, chaves primrias e estrangeiras, alm das respectivas referncias estrangeiras. Em nosso sistema teremos as seguintes tabelas mapeadas e seus campos: Cliente

24

Armazena os dados cadastrais dos clientes.


Campo Descrio Tipo

codCliente cpf email renda classe

PK - Cdigo que identifica o cliente CPF do cliente E-mail do cliente Renda mensal do cliente, em Reais Classe social do cliente, calculada de acordo com arenda

Long(campo chave) String(11) String(50) String(50) Currency String(1)

nomeCliente Nome completo do cliente

Produto Armazena as informaes sobre os produtos.


Campo Descrio Tipo

codProduto descricao unidade valorUnitario estoqueMinimo qtdEstoque

PK - Cdigo que identifica o produto Descrio do produto Unidade de comercializao do produto Valor unitrio do produto, em Reais Quantidade mnima desejada no estoque Quantidade atual do estoque

Long(campo chave) String(30) String(5) Currency Double Double

Venda Armazena informaes sobre as vendas realizadas.


Campo Descrio Tipo

codVenda

PK - Cdigo que identifica a venda

Long(campo chave) Long Date/Time

codCliente FK - Cdigo que indica o cliente relativo venda dataVenda Data de realizao da venda

DetalheVenda Armazena informao de ligao entre a Venda e os seus Produtos integrantes.


Campo Descrio Tipo

25

codProduto

PK - FK - Cdigo que indica o produto relacionado

Long(campo chave) FK: Produto(codPoduto) Long(campo chave) FK: Venda(codvenda) Double

codVenda PK - FK - Cdigo que indica a venda relacionada qtdProduto Quantidade do produto relacionado

Obs: PK: Primary Key (chave primria) / FK: Foreign Key (chave estrangeira)

Diagrama de Entidade-Relacionamento Para facilitar a visualizao e permitir que o modelo de dados seja melhor compreendido o dicionrio de dados convertido em um desenho que apresenta as tabelas e seus campos de forma grfica. Desta maneira a percepo das relaes fica bem mais simples. Este o Diagrama de Entidade-Relacionamento (DER) do nosso sistema de vendas:

Assim como a modelagem de classes utilizando a UML, a modelagem do diagrama de entidade-relacionamento no ser abordado em detalhes neste curso. Ento mais uma vez para se aprofundar no assunto procure material especializado em livros ou na Internet, ou aguarde outro artigo em um futuro prximo. Concluso Este artigo teve o objetivo de iniciar realmente a construo do sistema que ser objeto de estudo desta srie, atravs da definio de quais sero as partes componentes do projeto. Apesar de no nos aprofundarmos no assunto foi possvel termos uma noo de utilizao de padres de nomenclatura e da modelagem com a UML, conhecendo o diagrama de classes e percebendo a diferena entre os nveis de visualizao do sistema pelo detalhamento das informaes nos modelos. Alm disso conhecemos um pouco sobre o mapeamento das classes para o modelo relacional, utilizando o dicionrio de dados e o diagrama de entidade-relacionamento como ferramentas.
26

Mesmo no abrangendo todas as etapas do desenvolvimento de um software ficou clara a importncia de se planejar bem o que dever ser construdo, atravs da modelagem e da descrio dos itens, tarefa esta que faz parte da fase de anlise do sistema. Bem, como tudo j est definido a nossa etapa seguinte ser colocar a mo na massa, iniciando pela codificao das classes auxiliares, que sero teis para o funcionamento das restantes.

27

Classes IV - As Classes Auxiliares

No ltimo artigo vimos a modelagem do sistema. Agora chegou a hora de comearmos a codificar nossas primeiras classes. As classes auxiliares sero responsveis por realizar tarefas genricas que atendem a solicitaes de diversas outras classes ou mesmo outros mdulos do sistema. Poderamos criar inmeras classes auxiliares, com funes agrupadas pelo tipo de tarefa a ser realizada, assim como a classe ConexaoBD que trata de questes de conexo com banco de dados e a classe Utilitario que contempla funes de validao e converso de dados.

A idia principal aqui a reutilizao de cdigo, um dos pilares da orientao a objetos. Percebam que ns codificaremos a classe ConexaoBD apenas uma nica vez em toda a nossa vida. No prximo sistema que montarmos simplesmente a importaremos para o novo software, e a classe j dever estar pronta para ser usada. Pode ser que a codifiquemos para outro tipo de banco de dados, por exemplo uma ConexaoBDMySql ou uma ConexaoBDSqlServer, mas cada codificao ser nica e praticamente eterna. Vamos ento rever a descrio de cada classe. Em seguida faremos a codificao. O cdigo das funes ser apresentado apenas com comentrios mas sem detalhamento, j que (novamente) a criao de algoritmos e a estruturao de procedimentos e funes no o foco deste curso. A Classe Utilitario Objetivo: oferecer funcionalidades de validao de dados e transformao de valores, necessrias para o correto funcionamento da classe Cliente, ou qualquer outra classe ou funo que venha a precisar dela, a qualquer momento. Isto se deve ao fato da classe possuir mtodos genricos e reutilizveis, podendo ser reaproveitados, sem nenhuma alterao, em qualquer sistema. Mtodo validaCpf(argCpf As String) As Boolean: recebe como argumento um texto contendo um CPF, sem pontos nem trao, e devolve um valor booleano, verdadeiro ou falso, indicando se o CPF ou no vlido;
Function validaCPF(argCpf As String) As Boolean 'Funo que verifica a validade de um CPF. Dim Dim Dim Dim Dim Dim wSomaDosProdutos wResto wDigitChk1 wDigitChk2 wStatus wI

28

'Inicia o valor da Soma wSomaDosProdutos = 0 'Para posio I de 1 at 9 For wI = 1 To 9 'Soma = Soma + (valor da posio dentro do CPF x (11 - posio)) wSomaDosProdutos = wSomaDosProdutos + Val(Mid(argCpf, wI, 1)) * (11 wI) Next wI 'Resto = Soma - ((parte inteira da diviso da Soma por 11) x 11) wResto = wSomaDosProdutos - Int(wSomaDosProdutos / 11) * 11 'Dgito verificador 1 = 0 (se Resto=0 ou 1 ) ou 11 - Resto (nos casos restantes) wDigitChk1 = IIf(wResto = 0 Or wResto = 1, 0, 11 - wResto) 'Reinicia o valor da Soma wSomaDosProdutos = 0 'Para posio I de 1 at 9 For wI = 1 To 9 'Soma = Soma + (valor da posio dentro do CPF x (12 - posio)) wSomaDosProdutos = wSomaDosProdutos + (Val(Mid(argCpf, wI, 1)) * (12 wI)) Next wI 'Soma = Soma (2 x dgito verificador 1) wSomaDosProdutos = wSomaDosProdutos + (2 * wDigitChk1) 'Resto = Soma - ((parte inteira da diviso da Soma por 11) x 11) wResto = wSomaDosProdutos - Int(wSomaDosProdutos / 11) * 11 'Dgito verificador 2 = 0 (se Resto=0 ou 1 ) ou 11 - Resto (nos casos restantes) wDigitChk2 = IIf(wResto = 0 Or wResto = 1, 0, 11 - wResto) 'Se o dgito da posio 10 = Dgito verificador 1 E 'dgito da posio 11 = Dgito verificador 2 Ento If Mid(argCpf, 10, 1) = Mid(Trim(Str(wDigitChk1)), 1, 1) And _ Mid(argCpf, 11, 1) = Mid(Trim(Str(wDigitChk2)), 1, 1) Then 'CPF vlido validaCPF = True Else 'CPF invlido validaCPF = False End If End Function

Mtodo validaEmail(argEmail As String) As Boolean: recebe como argumento um texto contendo um e-mail e devolve um valor booleano, verdadeiro ou falso, indicando se o e-mail ou no vlido, sendo que a verificao feita apenas no seu formato;
Function validaEmail(eMail As String) As Boolean 'Funo de validao do formato de um e-mail. Dim posicaoA As Integer Dim posicaoP As Integer

29

'Busca posio do caracter @ posicaoA = InStr(eMail, "@") 'Busca a posio do ponto a partir da posio 'do @ ou ento da primeira posio posicaoP = InStr(posicaoA Or 1, eMail, ".") 'Se a posio do @ for menor que 2 OU 'a posio do ponto for menor que a posio 'do caracter @ If posicaoA < 2 Or posicaoP < posicaoA Then 'Formato de e-mail invlido validaEmail = False Else 'Formato de e-mail vlido validaEmail = True End If End Function

Mtodo nomeProprio(argNome As String) As String: recebe como argumento um texto qualquer e devolve o mesmo texto com as inicias dos nomes em maisculas e o restante em minsculas, levando em considerao as partculas de ligao de nomes, as quais permanecem em minsculas;
Function nomeProprio(argNome As String) As String 'Funo recursiva para converter a primeira letra 'dos nomes prprios para maiscula, mantendo os 'aditivos em caixa baixa. Dim sNome As String Dim lEspaco As Long Dim lTamanho As Long 'Pega o tamanho do nome lTamanho = Len(argNome) 'Passa tudo para caixa baixa argNome = LCase(argNome) 'Se o nome passado vazio 'acaba a funo ou a recurso 'retornando string vazia If lTamanho = 0 Then nomeProprio = "" Else 'Procura a posio do primeiro espao lEspaco = InStr(argNome, " ") 'Se no tiver pega a posio da ltima letra If lEspaco = 0 Then lEspaco = lTamanho 'Pega o primeiro nome da string sNome = Left(argNome, lEspaco) 'Se no for aditivo converte a primeira letra If Not InStr("e da das de do dos ", sNome) > 0 Then sNome = UCase(Left(sNome, 1)) & LCase(Right(sNome, Len(sNome) - 1)) End If

30

'Monta o nome convertendo o restante atravs da recurso nomeProprio = sNome & nomeProprio(LCase(Trim(Right(argNome, lTamanho - lEspaco)))) End If End Function

Mtodo desacentua(argTexto As String) As String: recebe como argumento um texto qualquer e devolve o mesmo texto sem acentos ou smbolos;
Function desacentua(ByVal argTexto As String) As String 'Funo que retira acentos de qualquer texto. Dim Dim Dim Dim Dim Dim strAcento As String strNormal As String strLetra As String strNovoTexto As String intPosicao As Integer i As Integer

'Informa as duas sequncias de caracteres, com e sem acento strAcento = "" strNormal = "AAAAAEEEEIIIIOOOOOUUUUYCNaaaaaeeeeiiiiooooouuuuycn" 'Retira os espaos antes e aps argTexto = Trim(argTexto) 'Para i de 1 at o tamanho do texto For i = 1 To Len(argTexto) 'Retira a letra da posio atual strLetra = Mid(argTexto, i, 1) 'Busca a posio da letra na sequncia com acento intPosicao = InStr(1, strAcento, strLetra) 'Se a posio for maior que zero If intPosicao > 0 Then 'Retira a letra na mesma posio na 'sequncia sem acentos. strLetra = Mid(strNormal, intPosicao, 1) End If 'Remonta o novo texto, sem acento strNovoTexto = strNovoTexto & strLetra Next 'Devolve o resultado desacentua = strNovoTexto End Function

Mtodo abreviaNome(argNome As String) As String: recebe como argumento um texto qualquer e devolve o mesmo texto com a penltima parte do nome abreviada, levando em considerao as partculas de ligao de nomes, as quais permanecem inalteradas.

31

Function abreviaNome(argNome As String) As String 'Funo que abrevia o penltimo sobrenome, levando 'em considerao os aditivos de, da, do, dos, das, e. 'Define variveis para controle de posio e para as 'partes do nome que sero separadas e depois unidas 'novamente. Dim ultimoEspaco As Integer, penultimoEspaco As Integer Dim primeiraParte As String, ultimaParte As String Dim parteNome As String Dim tamanho As Integer, i As Integer 'Tamanho do nome passado 'no argumento tamanho = Len(argNome) 'Loop que verifica a posio do ltimo e do penltimo 'espaos, utilizando apenas um loop. For i = tamanho To 1 Step -1 If Mid(argNome, i, 1) = " " And ultimoEspaco <> 0 Then penultimoEspaco = i Exit For End If If Mid(argNome, i, 1) = " " And penultimoEspaco = 0 Then ultimoEspaco = i End If Next i 'Caso i chegue a zero no podemos 'abreviar o nome If i = 0 Then abreviaNome = argNome Exit Function End If 'Separao das partes do nome em trs: primeira, meio e ltima primeiraParte = Left(argNome, penultimoEspaco - 1) parteNome = Mid(argNome, penultimoEspaco + 1, ultimoEspaco penultimoEspaco - 1) ultimaParte = Right(argNome, tamanho - ultimoEspaco) 'Para a montagem do nome j abreviado verificamos se a parte retirada 'no um dos nomes de ligao: de, da ou do. Caso seja usamos o mtodo 'recursivo para refazer os passos. 'Caso seja necessrio basta acrescentar outros nomes de ligao para serem 'verificados. If parteNome = "da" Or parteNome = "de" Or parteNome = "do" Or _ parteNome = "dos" Or parteNome = "das" Or parteNome = "e" Then abreviaNome = abreviaNome(primeiraParte & " " & parteNome) & " " & ultimaParte Else abreviaNome = primeiraParte & " " & Left(parteNome, 1) & ". " & ultimaParte End If End Function

A Classe ConexaoBD
32

Objetivo: oferecer funcionalidades de consulta e atualizao do banco de dados. Possui tambm mtodos genricos que podem ser reaproveitados em qualquer sistema. Uma das maiores vantagens de se utilizar uma classe de conexo que no caso de mudana do tipo de Sistema Gerenciador de Banco de Dados (SGBD) somente ela dever ser alterada, permanecendo todas as outras intocadas, desde que seja mantida a interface de comunicao dos mtodos. Um exemplo seria a troca do back-end para MySql, Postgres ou Sql Server. Declarao de Atributo de Banco de Dados a Nvel de Classe Criaremos um atributo que ser responsvel por receber e disponibilizar o banco de dados para ser utilizado pelos mtodos da classe.
'Cria um database que ser utilizado para toda a classe Private db As Database

Funo logicoSql(ByVal argValor As Boolean) As String: recebe como parmetro um valor booleano e devolve um texto contendo os valores True ou False, necessrio para cdigos SQL;
Function logicoSql(ByVal argValor As Boolean) As String 'Funo que troca os valores lgicos Verdadeiro/Falso 'para True/False para utilizao em consultas SQL 'Se o valor for verdadeiro If argValor Then 'Troca por True logicoSql = "True" Else 'Seno troca por False logicoSql = "False" End If End Function

Funo pontoVirgula(ByVal varValor As Variant) As String: recebe como parmetro um valor decimal em que o padro de separao da parte inteira e a decimal a vrgula, e devolve um texto contendo o mesmo valor agora separado por ponto, necessrio para cdigos SQL;
Function pontoVirgula(ByVal varValor As Variant) As String 'Funo que troca a vrgula de um valor decimal por 'um ponto para utilizao em consultas SQL Dim Dim Dim Dim strValor As String strInteiro As String strDecimal As String intPosicao As Integer

'Converte o valor em string strValor = CStr(varValor) 'Busca a posio da vrgula intPosicao = InStr(strValor, ",")

33

'Se h uma vrgula em alguma posio If intPosicao > 0 Then 'Retira a parte inteira strInteiro = Left(strValor, intPosicao - 1) 'Retira a parte decimal strDecimal = Right(strValor, Len(strValor) - intPosicao) 'Junta os dois novamente incluindo 'agora o ponto no lugar da vrgula pontoVirgula = strInteiro & "." & strDecimal Else 'Seno devolve o mesmo valor pontoVirgula = strValor End If End Function

Funo dataSql(ByVal argData As Date) As String: recebe como parmetro uma data em qualquer formato e devolve um texto contendo a data no padro #mm/dd/yyyy#, necessrio para cdigos SQL;
Function dataSql(ByVal argData As Date) As String 'Funo que formata uma data para o modo SQL 'com a cerquilha: #mm/dd/yyyy# Dim strDia As String, strMes As String, strAno As String 'Retira dia, ms e ano strDia = Day(argData) strMes = Month(argData) strAno = Year(argData) 'Remonta no formato adequado dataSql = "#" & strMes & "/" & strDia & "/" & strAno & "#" End Function

Funo valorSql(ByVal argValor As Variant) As String: recebe como parmetro um valor qualquer, verifica o seu formato e devolve um valor no formato padro dos cdigos SQL, fazendo uso das funes anteriores, caso necessrio;
Function valorSql(ByVal argValor As Variant) As String 'Funo que formata valores para utilizao 'em consultas SQL 'Seleciona o tipo de valor informado Select Case VarType(argValor) 'Caso seja vazio ou nulo apenas 'devolve a string Null Case vbEmpty, vbNull valorSql = "Null" 'Caso seja inteiro ou longo apenas 'converte em string Case vbInteger, vbLong valorSql = CStr(argValor) 'Caso seja simples, duplo, decimal ou moeda

34

'substitui a vrgula por ponto Case vbSingle, vbDouble, vbDecimal, vbCurrency valorSql = pontoVirgula(argValor) 'Caso seja data chama a funo dataSql() Case vbDate valorSql = dataSql(argValor) 'Caso seja string acrescenta aspas simples Case vbString valorSql = "'" & argValor & "'" 'Caso seja lgico chama a funo logicoSql() Case vbBoolean valorSql = logicoSql(argValor) End Select End Function

Funo executa(codigoSql As String) As Long: recebe como argumento um texto contendo um cdigo SQL de insero, excluso ou alterao, executa o cdigo e devolve o nmero de registros afetados na operao;
Function executa(codigoSql As String) As Long On Error GoTo Err_executa 'Atualiza dados no Banco utilizando o cdigo Sql passado funo, 'retornando o nmero de registros afetados caso a operao ocorra 'com sucesso ou 0 caso ocorra algum problema Set db = Currentdb db.Execute codigoSql executa = db.RecordsAffected Exit_executa: Set db = Nothing Exit Function Err_ executa: executa = 0 Resume Exit_executa End Function

Funo consulta(codigoSql As String, Optional editavel As Boolean = False) As Recordset: recebe como argumento um texto contendo um cdigo SQL de consulta de registros e um valor lgico que define o modo de bloqueio de execuo, permitindo ou no a edio dos dados durante a operao, e devolve um recordset contendo o conjunto de registros que atenderem aos critrios;
Function consulta(codigoSql As String, Optional editavel As Boolean = False) As Recordset On Error GoTo Err_consulta 'Consulta os dados no Banco utilizando o cdigo Sql passado funo, 'retornando um recordset com o resultado da consulta Set db = Currentdb If editavel Then

35

Set consulta = db.OpenRecordset(codigoSql, , dbInconsistent, dbOptimistic) Else Set consulta = db.OpenRecordset(codigoSql, dbOpenSnapshot, dbReadOnly) End If Exit_consulta: Set db = Nothing Exit Function Err_consulta: MsgBox "Erro SQL: " & codigoSql Set consulta = Nothing Resume Exit_consulta End Function

Aps criar os mtodos salve as classes, mas no se esquea que para salvar convencionamos que seria utilizado o prefixo acl para classes auxiliares. Ento salvaremos as classes com os nomes aclUtilitario e aclConexaoBD. Instanciao dos Objetos Com as classes j codificadas e salvas podemos instanci-las a partir de qualquer mdulo do nosso aplicativo. Vamos fazer um teste com cada uma das classes criadas. Para isto voltaremos ao nosso mdulo padro TesteClasse. Primeiro a classe Utilitario. Vamos criar o procedimento TesteUtilitario:
Sub TesteUtilitario() 'Declarando o objeto da classe Utilitario Dim objUtil As New aclUtilitario 'Testando um mtodo da classe, convertendo um 'nome em minsculas para o padro de nomes prprios 'com a primeira letra maiscula MsgBox objUtil.nomeProprio("plinio mabesi") End Sub

Perceba que aps digitarmos o ponto depois do nome do objeto os mtodos disponveis so listados no menu suspenso do editor do VBA.

36

Com isso voc pode digitar o nome do mtodo ou escolher um deles na lista suspensa. Conforme j vimos antes para executar o cdigo basta posicionar o cursor dentro do procedimento e pressionar F5, ou F8 para acompanhar a execuo passo-a-passo. O resultado ser uma caixa de mensagem exibindo o nome informado no formato de um nome prprio, com as iniciais maisculas:

Agora a classe ConexaoBD. Vamos criar o procedimento TesteConexao:


Sub TesteConexaoBD() 'Declarando o objeto da classe ConexaoBD Dim objCon As New aclConexaoBD 'Testando um mtodo da classe, convertendo 'uma data para o padro SQL MsgBox objCon.dataSql("12/06/10") End Sub

Assim como no objeto da classe Utilitario, ao digitarmos o ponto aps o nome do objeto da classe ConexaoBD veremos a lista suspensa com os mtodos disponveis:

37

Com certeza voc notou que o atributo db (Database) no foi apresentado, devido ao fato de ser um atributo privado (Private). Perceba tambm que quando digitamos o primeiro parntese aps o nome do mtodo o editor do VBA nos mostra quais so os parmetros exigidos, o seu tipo e o tipo de dado de retorno do mtodo. No caso do mtodo dataSql() deveremos informar o parmetro argData que ser passado por valor (ByVal) e do tipo Date, e teremos como retorno uma String. Veja:

Ao executarmos este procedimento teremos como resultado uma caixa de mensagem exibindo a data informada no padro de utilizao do SQL, conforme figura abaixo.

38

No padro SQL veja que o ms vem em primeiro lugar, seguido pelo dia e depois pelo ano, tudo delimitado pela cerquilha. Todos os cdigos SQL devem estar com as datas neste formato, caso contrrio ocorrer um erro e o cdigo no ser executado. Voc no s pode como deve fazer testes com os outros mtodos. Crie exemplos de utilizao para cada um deles. Se encontrar dificuldade aguarde os prximos artigos, nos quais faremos uso intenso destas duas classes. Mesmo assim o melhor nunca desistir... Concluso Nosso sistema comea a tomar forma de um software de verdade. Nesta etapa vimos como codificar as classes auxiliares e tivemos uma noo de como instanci-las para utilizar os mtodos disponibilizados. Apesar de no ter sido detalhada a criao da estrutura dos comandos e dos algoritmos dos mtodos, os comentrios o ajudaro a compreender o mnimo necessrio para produzir seus prprios cdigos. Caso no tenha entendido algum trecho pesquise sobre as funes, comandos e tipos utilizados. Conforme salientado no incio deste artigo, o principal conceito que deve ser lembrado no momento o da reutilizao de cdigo. A reutilizao se refere a qualquer procedimento, funo ou mtodo que possa ser diretamente empregado em qualquer projeto imediatamente, sem necessidade de ajuste ou adaptao. Com a orientao a objetos procuramos fazer isto com classes inteiras. As classes Utilitario e ConexaoBD so dois exemplos de classes reutilizveis que no requerem qualquer modificao para funcionarem em um projeto do Access. Claro que algum poderia dizer que estaria faltando um ou outro mtodo ou atributo, ou que na verdade os mtodos existentes no so os melhores, mas o importante no momento o conceito da reutilizao e no a modelagem mais eficiente e mais correta. Entenda que aps projetar a classe mais perfeita possvel e codific-la, ento ela poder ser distribuda para ser importada e incorporada por qualquer projeto.

39

Classes V - A Classe Cliente


Chegou enfim a hora de criarmos a primeira parte funcional do nosso sistema. Depois de todo o sistema estar modelado e tambm estarem codificadas as classes auxiliares, vamos ento dar vida ao programa. A primeira classe a ser codificada ser a classe Cliente. Aps apresentar a estrutura da classe faremos a implementao da interface grfica que manipular os dados dos objetos. Caso necessrio relembre os conceitos anteriores sobre atributos, mtodos, acesso s propriedades do objeto, instncias e demais ensinamentos sobre a programao orientada a objetos, vistos nos primeiros artigos. A Classe Cliente O objetivo desta classe, conforme dito anteriormente, oferecer funcionalidades de incluso, consulta, atualizao e excluso dos objetos do tipo cliente. Este o cdigo da classe, com todos os seus atributos e mtodos, que se utilizam de funes prprias, bem como dos mtodos dos objetos das classes Utilitario e ConexaoBD. Cdigo da classe:
Option Compare Database Option Explicit 'Objeto da classe Utilitario Private objUtil As New aclUtilitario 'Atributos da Classe 'Atributo de backup e atributo identificador da Classe 'PK - Cdigo que identifica o cliente. Private bkpCodCliente As Variant Private lngCodCliente As Variant 'Classe social do cliente, calculada de acordo com a renda. Private strClasse As Variant 'CPF do cliente. Private strCpf As Variant 'E-mail do cliente. Private strEmail As Variant 'Nome completo do cliente. Private strNomeCliente As Variant 'Renda mensal do cliente, em reais. Private curRenda As Variant 'Mtodos Get, Set e Let da Classe

40

Property Get codCliente() As Variant codCliente = lngCodCliente End Property Property Let codCliente(argCodCliente As Variant) lngCodCliente = argCodCliente If IsEmpty(bkpCodCliente) Then bkpCodCliente = lngCodCliente End If End Property Property Get classe() As Variant classe = strClasse End Property Property Get cpf() As Variant cpf = strCpf End Property Property Let cpf(argCpf As Variant) If Not IsNull(argCpf) Then If Not objUtil.validaCPF(argCpf) Then MsgBox "O CPF <" & Format(argCpf, "000\.000\.000\-00") & _ "> no vlido.", vbExclamation, "CPF Invlido" strCpf = Null Else strCpf = argCpf End If Else strCpf = argCpf End If End Property Property Get email() As Variant email = strEmail End Property Property Let email(argEmail As Variant) If Not IsNull(argEmail) Then If Not objUtil.validaEmail(argEmail) Then If MsgBox("O E-mail <" & argEmail & "> no possui um formato vlido." _ & vbCrLf & "Deseja incluir este e-mail?", vbQuestion + vbYesNo, _ "E-mail Invlido") = vbYes Then strEmail = argEmail

41

End If Else strEmail = argEmail End If Else strEmail = argEmail End If End Property Property Get nomeCliente() As Variant nomeCliente = strNomeCliente End Property Property Let nomeCliente(argNomeCliente As Variant) If Not IsNull(argNomeCliente) Then strNomeCliente = objUtil.nomeProprio(argNomeCliente) Else strNomeCliente = argNomeCliente End If End Property Property Get renda() As Variant renda = curRenda End Property Property Let renda(argRenda As Variant) curRenda = argRenda 'Alterar classe quando o valor da 'renda for alterado strClasse = calculaClasse() End Property 'Mtodo Existe [Com conhecimento de SQL] 'Verifica a existncia do objeto Cliente na tabela correspondente 'no Banco de Dados Function existe(argCodCliente As Variant) As Boolean On Error GoTo Err_existe Dim objCon As New aclConexaoBD Dim rstExiste As Recordset Dim strSql As String existe = False strSql = "Select * " & _ "From Cliente " & _ "Where codCliente = " & objCon.valorSql(argCodCliente) Set rstExiste = objCon.consulta(strSql) If rstExiste.RecordCount > 0 Then existe = True

42

End If 'Fecha o Recordset existe rstExiste.Close Exit_existe: Set rstExiste = Nothing Exit Function Err_existe: existe = False GoTo Exit_existe End Function 'Mtodo Incluir [Com conhecimento de SQL] 'Inclui um novo objeto na tabela correspondente dentro do Banco de dados Private Function incluir() As Boolean On Error GoTo Err_incluir Dim objCon As New aclConexaoBD Dim strSql As String strSql = "Insert Into " & _ "Cliente(codCliente,classe,cpf,email,nomeCliente,renda) " & _ "Values(" & objCon.valorSql(codCliente) & "," _ & objCon.valorSql(classe) & "," & objCon.valorSql(cpf) _ & "," & objCon.valorSql(email) & "," & objCon.valorSql(nomeCliente) _ & "," & objCon.valorSql(renda) & ")" incluir = (objCon.executa(strSql) > 0) If incluir Then 'Atualiza os campos de backup bkpCodCliente = codCliente End If Exit_incluir: Exit Function Err_incluir: incluir = False GoTo Exit_incluir End Function 'Mtodo Excluir [Com conhecimento de SQL] 'Exclui o objeto atual na tabela correspondente dentro do Banco de dados Function excluir() As Boolean On Error GoTo Err_excluir Dim objCon As New aclConexaoBD Dim strSql As String strSql = "Delete From Cliente " & _ "Where codCliente = " & objCon.valorSql(codCliente) excluir = (objCon.executa(strSql) > 0)

43

Exit_excluir: Exit Function Err_excluir: excluir = False GoTo Exit_excluir End Function 'Mtodo Obter [Com conhecimento de SQL] 'Recupera o objeto Cliente atravs dos argumentos informados Function obter(argCodCliente As Variant) As Boolean On Error GoTo Err_obter Dim objCon As New aclConexaoBD Dim rstObter As Recordset Dim strSql As String strSql = "Select * " & _ "From Cliente " & _ "Where codCliente = " & objCon.valorSql(argCodCliente) Set rstObter = objCon.consulta(strSql) If rstObter.RecordCount = 0 Then obter = False Exit Function End If 'Atualiza os campos de backup e os identificadores codCliente = argCodCliente bkpCodCliente = argCodCliente 'Atualiza os campos restantes strClasse = rstObter.Fields("classe") cpf = rstObter.Fields("cpf") strEmail = rstObter.Fields("email") nomeCliente = rstObter.Fields("nomeCliente") renda = rstObter.Fields("renda") obter = True 'Fecha o Recordset obter rstObter.Close Exit_obter: Set rstObter = Nothing Exit Function Err_obter: obter = False MsgBox Err.Description GoTo Exit_obter End Function 'Mtodo Salvar [Com conhecimento de SQL] 'Salva o objeto atual na tabela correspondente dentro do Banco de dados

44

Function salvar() As Boolean On Error GoTo Err_salvar Dim objCon As New aclConexaoBD Dim strSql As String If existe(bkpCodCliente) Then strSql = "Update Cliente " & _ "Set codCliente = " & objCon.valorSql(codCliente) _ & ", classe = " & objCon.valorSql(classe) _ & ", cpf = " & objCon.valorSql(cpf) & ", email = " _ & objCon.valorSql(email) & ", nomeCliente = " _ & objCon.valorSql(nomeCliente) & ", renda = " _ & objCon.valorSql(renda) _ & " Where codCliente = " & objCon.valorSql(bkpCodCliente) salvar = (objCon.executa(strSql) > 0) Else salvar = incluir End If If salvar Then 'Atualiza as variveis de backup com o novo valor da chave bkpCodCliente = codCliente End If Exit_salvar: Exit Function Err_salvar: salvar = False GoTo Exit_salvar End Function 'Mtodo calculaClasse 'Calcula a classe social do objeto atual baseado na 'renda informada e devolve uma letra correspondente ' sua classificao dentro das faixas salariais. Private Function calculaClasse() As String If renda >= 15300 Then calculaClasse = "A" ElseIf renda >= 7650 Then calculaClasse = "B" ElseIf renda >= 3060 Then calculaClasse = "C" ElseIf renda >= 1020 Then calculaClasse = "D" ElseIf renda > 0 Then calculaClasse = "E" Else calculaClasse = "" End If End Function 'Fim da classe...

Pontos Importantes
45

Nesta primeira classe referente a um objeto atualizvel, com atributos modificveis, passveis de serem persistidos, podemos notar alguns pontos de interesse para o aprendizado da metodologia, conforme a seguir. 1. Atributos do tipo Variant: Como podemos encontrar objetos cujos campos no so de preenchimento obrigatrio devemos manter a possibilidade de atribuir valores nulos aos atributos dos objetos, os quais sero reconhecidos pelo mtodo valorSql(), da classe ConexaoBD, e convertidos da maneira correta para armazenagem na tabela. Caso utilizssemos atributos com os tipos pr-definidos no teramos como reconhecer um atributo nulo de maneira to fcil. Neste caso teramos que procurar mtodos alternativos de conveno e tratamento de valores que representariam a nulidade de atributos, como por exemplo uma seqncia de comprimento zero em uma string, ou um valor negativo que provavelmente nunca seria atribudo a um campo de cdigo para chave primria, entre outros. Contudo, para o nosso projeto, contando com a facilidade da atribuio de valores e seus tipos s variveis do tipo Variant, e a funcionalidade do mtodo valorSql() esta ser a forma que adotaremos.
'Nome completo do cliente. Private strNomeCliente As Variant

2. Criao do atributo de backup do objeto: Apesar de no ser necessrio quando se usa atributos identificadores do tipo auto-numerao, esta uma tcnica que possibilita a alterao do cdigo identificador do objeto permitindo salvar as modificaes posteriormente, j que o antigo valor, que continua armazenado no banco de dados, ainda estar disponvel no atributo de backup. Quando se usa qualquer tipo de cdigo personalizado como a chave primria este recurso torna-se indispensvel.
'Atributo de backup e atributo identificador da Classe 'PK - Cdigo que identifica o cliente. Private bkpCodCliente As Variant Private lngCodCliente As Variant

3. Validao de dados no momento da atribuio do valor: Neste caso podemos visualizar na prtica como validar um dado antes de atribu-lo ao objeto. Como exemplo verificamos a validade de um CPF fornecido pelo usurio, permitindo ou no a sua utilizao. Esta uma tarefa que evita falhas de digitao e incorrees nos dados armazenados.
Property Let cpf(argCpf As Variant) If Not IsNull(argCpf) Then If Not objUtil.validaCPF(argCpf) Then MsgBox "O CPF <" & Format(argCpf, "000\.000\.000\-00") _ & "> no vlido.", vbExclamation, "CPF Invlido" strCpf = Null Else strCpf = argCpf End If Else strCpf = argCpf End If End Property

46

4. Converso de dados no momento da atribuio do valor: Neste caso podemos visualizar na prtica como alterar um dado fornecido pelo usurio antes de atribu-lo ao objeto. Esta tarefa til na padronizao das informaes armazenadas. Como exemplo armazenamos todos os nomes dos clientes no padro de nomes prprios, com a primeira letra de cada parte do nome em maisculas e o restante em minsculas. Esta funcionalidade oferecida pelo mtodo nomeProprio() da classe Utilitario, representada pelo objeto objUtil.
Property Let nomeCliente(argNomeCliente As Variant) If Not IsNull(argNomeCliente) Then strNomeCliente = objUtil.nomeProprio(argNomeCliente) Else strNomeCliente = argNomeCliente End If End Property

5. Determinao de uma propriedade baseada em outra: Apesar de no ser uma boa prtica e estar diretamente contra as Formas Normais, esta funcionalidade foi colocada apenas para servir de exemplo. Aqui determinamos a classe social a que o cliente pertence a partir da sua renda informada. Para isto utilizamos o mtodo calculaClasse() da prpria classe Cliente. A informao atribuda propriedade classe e armazenada no banco de dados, lembrando mais uma vez que esta no uma prtica recomendada em sistemas profissionais.
Property Let renda(argRenda As Variant) curRenda = argRenda 'Alterar classe quando o valor da 'renda for alterado strClasse = calculaClasse() End Property

6. Ajuste de cdigo SQL: Para que possamos montar nossas classes sem nos preocupar com o formato SQL dos dados, utilizamos a classe ConexaoBD e seus mtodos para fazer todos os ajustes e nos deixar resolver os problemas mais srios, que requerem nossa ateno. Veja que para ns no ser necessrio descobrir se o atributo e do tipo texto, data ou numrico. O mtodo valorSql() se encarrega disto. Aqui a classe est representada pelo objeto objCon.
strSql = "Select * " & _ "From Cliente " & _ "Where codCliente = " & objCon.valorSql(argCodCliente)

7. Atributo classe: Para o clculo da classe social a partir da renda foi utilizada uma tabela fictcia de exemplo, a qual no representa fielmente os valores publicados pelos rgos competentes. Outra funcionalidade importante que podemos encontrar nos cdigos o tratamento de erros com a instruo On Error. Apesar de no ser detalhado neste curso altamente recomendado que o leitor pesquise e compreenda a sua utilizao. Foram colocados exemplos simples nos mtodos, porm o correto entendimento deste recurso possibilita
47

alta capacidade de resoluo de problemas na programao, evitando que possveis e provveis erros no projeto ou na implementao sejam visveis para o usurio final de maneira inadequada. Mais Funes Auxiliares Neste projeto no utilizaremos cdigos com auto-numerao diretamente nas tabelas. Sendo assim necessitaremos de uma funo que realize esta tarefa. Criaremos ento um mdulo padro que conter todas as funes auxiliares que necessitarmos durante o curso. Ele se chamar Funcoes e nossa primeira funo ser chamada proximoCodigo(). Este ser o cdigo da funo:
Function proximoCodigo(argCampo As String, argTabela As String, _ Optional criterio As String = "") As Long proximoCodigo = Nz(DMax(argCampo, argTabela, criterio), 0) + 1 End Function

Ela receber como parmetros o nome do campo, o nome da tabela e um critrio de busca opcional, devolvendo um nmero que ser o prximo cdigo livre daquele campo numrico da tabela informada. Desta maneira a funo ser genrica e servir para qualquer tabela que contenha um campo chave de cdigo numrico ordenado. A Interface Grfica Para que possamos visualizar e manipular os dados dos clientes devemos criar um formulrio com campos para preenchimento e botes de comando que coordenem as aes sobre os dados. A seguir sero definidas as caractersticas para o formulrio no qual iremos trabalhar com os dados dos objetos relativos aos clientes. As especificaes de design so apenas sugestes, porm as informaes de origem de dados, bloqueio de campos e formatos de dados so obrigatrias, sob pena de no obter a funcionalidade desejada. Estas especificaes estaro marcadas com um asterisco vermelho ao lado. No formulrio teremos uma lista contendo os clientes cadastrados, um campo de texto para realizar buscas, alguns campos de texto para digitao das informaes e os botes de comando para criar, salvar e excluir registros. Obs: Somente as propriedades que foram alteradas para um valor diferente do padro sero apresentadas. Para as demais utilize o padro do sistema ou o valor que desejar, desde que no comprometa a funcionalidade do sistema. Formulrio
Nome: FCliente Largura: 20cm

48

Altura: Cabealho (1cm) / Detalhe (10cm) Legenda: Sistema de Vendas Estilo da borda: fino Seletores de registro: No Botes de navegao: No Linhas divisrias: No Barras de rolagem: Nenhuma Caixa de controle: Sim Boto Fechar: Sim Botes Min Max: Nenhum Popup: Sim Janela Restrita: No

Controle Listbox
Nome: lstCliente * Origem da linha: SELECT Cliente.codCliente, Cliente.nomeCliente, Format(Cliente.cpf,"000\.000\.000-00") AS cpf FROM Cliente ORDER BY Cliente.nomeCliente; * Tipo de Origem da Linha: Tabela/Consulta * Coluna acoplada: 1 * Nmero de colunas: 3 * Largura das colunas: 0cm;5cm;3cm * Largura: 8cm Cor do fundo: Cinza claro

Controles Textbox
Nome: txtPesquisa * Cor do fundo: Amarelo Alinhamento: Esquerda Nome: txtCodigo * Ativado: No * Bloqueado: Sim * Cor do fundo: Cinza Alinhamento: Centro Nome: txtCpf * Cor do fundo: Azul claro Mscara de entrada: 000.000.000\-00;;_ * Alinhamento: Centro Nome: txtNome * Cor do fundo: Azul claro Alinhamento: Esquerda Nome: txtEmail * Cor do fundo: Azul claro Alinhamento: Esquerda Nome: txtRenda * Cor do fundo: Azul claro Alinhamento: Direita Formato: Unidade Monetria Nome: txtClasse * Ativado: No *

49

Bloqueado: Sim * Cor do fundo: Cinza Alinhamento: Centro

Botes de Comando
Nome: btnNovo * Legenda: Novo Cor da Fonte: Azul escuro Nome: btnSalvar * Legenda: Salvar Cor da Fonte: Azul escuro Nome: btnExcluir * Legenda: Excluir Cor da Fonte: Vermelho escuro

Como sugesto de design os campos devero estar posicionados conforme demonstrado na figura a seguir, assim como devem ser adicionadas as legendas dos campos, as quais no foram descritas anteriormente:

Cdigos do Formulrio Para que nosso formulrio seja capaz de manipular os dados dos objetos cliente deveremos implementar as funcionalidades necessrias que permitam criar um novo cliente, buscar um cliente j cadastrado, editar seus dados, salv-lo ou exclu-lo. Sendo assim criaremos algumas funes genricas que sejam teis para quantos procedimentos delas necessitem, reaplicando o conceito da reutilizao de cdigo. Alm
50

disso incluiremos os cdigos dos campos que possuem eventos, alm dos botes de comando.
Todos os cdigos a seguir devero ser colocados no mdulo do formulrio FCliente.

1. Pesquisa de clientes: Para facilitar a busca de clientes por qualquer parte do nome utilizaremos um campo de texto cujo valor ser o elemento condicional para o cdigo SQL que ser atribudo ao controle lstCliente, atualizando sua propriedade Origem da Linha aps cada alterao realizada no campo de pesquisa. Para isto devemos colocar o cdigo no evento Ao Alterar do campo txtPesquisa.
Private Sub txtPesquisa_Change() lstCliente.RowSource = "SELECT Cliente.codCliente, Cliente.nomeCliente, " & _ "Format(Cliente.cpf,'000\.000\.000-00') AS cpf " & _ "FROM Cliente " & _ "WHERE nomeCliente Like '*" & txtPesquisa.Text & "*' " & _ "ORDER BY Cliente.nomeCliente;" lstCliente.Requery End Sub

2. Limpeza dos campos: Para efetuar a limpeza dos campos de preenchimento utilizaremos um procedimento genrico cuja funo ser a de atribuir o valor nulo a todos os campos de texto e enviar o foco ao campo txtCpf.
Private Sub limpaCampos() txtCodigo = Null txtCpf = Null txtNome = Null txtEmail = Null txtRenda = Null txtClasse = Null txtCpf.SetFocus End Sub

3. Novo registro: Para a criao de um novo registro em branco simplesmente iremos chamar o procedimento limpaCampos() no evento Ao Clicar do boto btnNovo.
Private Sub btnNovo_Click() Call limpaCampos End Sub

4. Atualizao de campos: Assim como fizemos um procedimento genrico que limpa os campos para a criao de um novo registro teremos tambm um que preencha os campos com as informaes de um objeto cliente passadas como parmetro, ou seja, o procedimento recebe um objeto completo e lana os valores de seus atributos nos campos do formulrio.
Private Sub atualizaCampos(argCliente As clsCliente)

51

txtCodigo = argCliente.codCliente txtCpf = argCliente.cpf txtNome = argCliente.nomeCliente txtEmail = argCliente.email txtRenda = argCliente.renda txtClasse = argCliente.classe End Sub

5. Escolha de cliente: J que teremos tambm que trabalhar com clientes j cadastrados, devemos implementar alguma maneira de recuperar suas informaes. Faremos isto utilizando a Listbox lstCliente, que utilizar o procedimento atualizaCampos() para preencher os campos com as informaes do objeto do cliente que foi clicado.
Private Sub lstCliente_Click() Dim objCliente As New clsCliente Dim codigoCliente As Long codigoCliente = lstCliente.Value If objCliente.obter(codigoCliente) Then Call atualizaCampos(objCliente) End If End Sub

6. Montagem de um objeto: Esta tambm uma funo genrica que ser utilizada por outros procedimentos ou funes. Seu objetivo criar um novo objeto cliente, coletar os dados digitados nos campos, atribuir ao objeto criado, e devolv-lo para quem chamou a funo.
Private Function buscaCampos() As clsCliente Set buscaCampos = New clsCliente If IsNull(txtCodigo) Then txtCodigo = proximoCodigo("codCliente", "Cliente") End If buscaCampos.codCliente = txtCodigo buscaCampos.cpf = txtCpf buscaCampos.nomeCliente = txtNome buscaCampos.email = txtEmail buscaCampos.renda = txtRenda End Function

7. Salvando um cliente: Depois de informados os dados de um novo cliente, ou alterados os dados de um cliente j cadastrado, necessitaremos de um procedimento que efetue a gravao destes dados. Para isto o boto btnSalvar conter o cdigo necessrio no evento Ao Clicar. Ele far uso da funo buscaCampos(), a qual montar o objeto a ser salvo, e tambm o mtodo salvar() do objeto, apresentando uma mensagem ao usurio com o resultado da operao. Aps salvar os dados de um cliente tambm

52

deveremos atualizar a Listbox para que apresente estes dados, sejam eles novos ou apenas alterados.
Private Sub btnSalvar_Click() Dim objCliente As clsCliente If Not IsNull(txtCpf) And Not IsNull(txtNome) Then Set objCliente = buscaCampos If objCliente.salvar Then MsgBox "O cliente foi salvo com sucesso.", vbInformation, _ "Salvar Cliente" lstCliente.Requery Call atualizaCampos(objCliente) Else MsgBox "Ocorreu um erro durante o salvamento.", vbExclamation, _ "Salvar Cliente" End If Else MsgBox "Informe os dados do cliente.", vbExclamation, "Salvar Cliente" End If End Sub

8. Excluindo um cliente: Alm de criar e alterar os dados de um cliente, importante que tambm possamos exclu-lo definitivamente de nosso cadastro. Logo devemos tambm implementar a funcionalidade para o boto btnExcluir, que conter o cdigo necessrio tambm no evento Ao Clicar. Ele tambm dever fazer uso da funo buscaCampos() para a montagem do objeto a ser excludo, pois caso contrrio a classe no saber que objeto deve ser excludo. Em seguida utilizaremos o mtodo excluir() do objeto, apresentando uma mensagem ao usurio com o resultado da operao. claro que aps excluir um cliente tambm deveremos atualizar a Listbox para que a mesma reflita as alteraes realizadas.
Private Sub btnExcluir_Click() Dim objCliente As clsCliente If Not IsNull(txtCodigo) Then If MsgBox("Confirma a excluso do registro?", vbQuestion + vbYesNo, _ "Excluir Cliente") = vbYes Then Set objCliente = buscaCampos If objCliente.excluir Then MsgBox "O registro foi excludo com sucesso.", vbInformation, _ "Excluir Cliente" lstCliente.Requery Call limpaCampos Else MsgBox "Ocorreu um erro durante a excluso.", vbExclamation, _ "Excluir Cliente" End If

53

End If End If End Sub

Estes so os procedimentos e funes bsicas para que a interface possa manipular os objetos. No se esquea de que para efetuar os testes deveremos utilizar um CPF vlido devido verificao realizada. Sistema de Exemplo A partir deste artigo ser disponibilizado o link para download do sistema de vendas no estado de desenvolvimento em que se encontrar o projeto. Recomendo aos estudiosos desenvolvedores que o consultem apenas para tirar dvidas e realizar comparaes. Aqueles que realmente quiserem aprender faam toda a codificao e a montagem das telas manualmente. Somente assim a sua mente ir se defrontar com as dificuldades do processo e assimilar os passos necessrios para a resoluo dos problemas e a concluso dos objetivos. Segue o link para download:
Venda00.zip

Como ainda no h uma tela principal abra o formulrio FCliente diretamente na janela Banco de Dados (Access 2003-) ou no Painel de Navegao (Access 2007+). Concluso Enfim demos incio parte mais empolgante do curso, na qual podemos ver o sistema realmente operando, manipulando os objetos. Pudemos ver como se d a interao entre objetos, a verificao de atributos, a montagem de cdigos SQL para busca, alterao, insero e excluso de dados, alm de visualizar procedimentos simples de tratamento de erros. Nunca demais relembrar que o objetivo maior de todas as etapas do curso apresentar de forma didtica, da maneira mais simples possvel, a utilizao de objetos em projetos do Access/VBA. Sendo assim no considere as implementaes apresentadas como o padro de fato da programao. Muita coisa foi simplesmente omitida ou modificada, mesmo partindo dos prprios padres que utilizo em meus projetos reais para que fosse alcanada a objetividade necessria ao entendimento por parte de usurios iniciantes ou com pouca experincia na POO. A lio mais importante deste artigo foi realmente a visualizao da dinmica da utilizao dos objetos na manipulao de informaes, tanto na sua apresentao quanto na sua persistncia. A partir deste momento, creio, ficar cada vez mais fcil a percepo da operao com objetos, desde que o leitor compreenda bem o que foi visto at agora, pois estes
54

conceitos iniciais so a base da orientao a objetos. Apesar de no contarmos com as outras faces da OO, como a herana por exemplo, com certeza a mente do leitor j comear a visualizar melhor as infinitas possibilidades que a metodologia fornece. Porm relembro que para conseguir dominar a tcnica imprescindvel continuar treinando, criando seus prprios cdigos, fazendo experincias, resolvendo problemas com a aplicao do paradigma, lendo outros materiais e compartilhando conhecimento, seja com colegas de trabalho, amigos ou em fruns sobre o assunto.

55

Classes VI - A Classe Produto


Continuando a incluso de funcionalidade em nosso sistema vamos agora ao prximo passo. A prxima classe a ser codificada ser a classe Produto. Aps apresentar a estrutura da classe faremos a implementao da interface grfica que manipular os dados dos objetos. Mais uma vez, caso necessrio, relembre os conceitos anteriores sobre atributos, mtodos, acesso s propriedades do objeto, instncias e demais ensinamentos sobre a programao orientada a objetos, vistos nos primeiros artigos.

A Classe Produto O objetivo desta classe, conforme dito anteriormente, oferecer funcionalidades de incluso, consulta, atualizao e excluso dos objetos do tipo produto. Este o cdigo da classe, com todos os seus atributos e mtodos, que se utilizam de funes prprias, bem como dos mtodos do objeto da classe ConexaoBD. Cdigo da classe:
Option Compare Database Option Explicit 'Atributos da Classe 'Atributo de backup e atributo identificador da Classe 'PK - Cdigo que identifica o produto. Private bkpCodProduto As Variant Private lngCodProduto As Variant 'Descrio do produto. Private strDescricao As Variant 'Quantidade mnima desejada no estoque. Private dblEstoqueMinimo As Variant 'Quantidade atual do estoque. Private dblQtdEstoque As Variant 'Unidade de comercializao do produto. Private strUnidade As Variant 'Valor unitrio do produto. Private curValorUnitario As Variant 'Mtodos Get, Set e Let da Classe Property Get codProduto() As Variant

56

codProduto = lngCodProduto End Property Property Let codProduto(argCodProduto As Variant) lngCodProduto = argCodProduto If IsEmpty(bkpCodProduto) Then bkpCodProduto = lngCodProduto End If End Property Property Get descricao() As Variant descricao = strDescricao End Property Property Let descricao(argDescricao As Variant) strDescricao = argDescricao End Property Property Get estoqueMinimo() As Variant estoqueMinimo = dblEstoqueMinimo End Property Property Let estoqueMinimo(argEstoqueMinimo As Variant) dblEstoqueMinimo = argEstoqueMinimo End Property Property Get qtdEstoque() As Variant qtdEstoque = dblQtdEstoque End Property Property Let qtdEstoque(argQtdEstoque As Variant) dblQtdEstoque = argQtdEstoque End Property Property Get unidade() As Variant unidade = strUnidade End Property Property Let unidade(argUnidade As Variant) strUnidade = Ucase(argUnidade) End Property

57

Property Get valorUnitario() As Variant valorUnitario = curValorUnitario End Property Property Let valorUnitario(argValorUnitario As Variant) curValorUnitario = argValorUnitario End Property 'Mtodo Existe [Com conhecimento de SQL] 'Verifica a existncia do objeto Produto na tabela 'correspondente no Banco de Dados. Function existe(argCodProduto As Variant) As Boolean On Error GoTo Err_existe Dim objCon As New aclConexaoBD Dim rstExiste As Recordset Dim strSql As String existe = False strSql = "Select * " & _ "From Produto " & _ "Where codProduto = " & objCon.valorSql(argCodProduto) Set rstExiste = objCon.consulta(strSql) If rstExiste.RecordCount > 0 Then existe = True End If 'Fecha o Recordset existe rstExiste.close Exit_existe: Set rstExiste = Nothing Exit Function Err_existe: existe = False GoTo Exit_existe End Function 'Mtodo Incluir [Com conhecimento de SQL] 'Inclui um novo objeto na tabela correspondente dentro do Banco de dados Function incluir() As Boolean On Error GoTo Err_incluir Dim objCon As New aclConexaoBD Dim strSql As String strSql = "Insert Into " & _ "Produto(codProduto,descricao,estoqueMinimo,qtdEstoque, _ unidade,valorUnitario) " & _ "Values(" & objCon.valorSql(codProduto) & "," & _

58

objCon.valorSql(descricao) & "," & _ objCon.valorSql(estoqueMinimo) & "," & _ objCon.valorSql(qtdEstoque) & "," & _ objCon.valorSql(unidade) & "," & _ objCon.valorSql(valorUnitario) & ")" incluir = (objCon.executa(strSql) > 0) If incluir Then 'Atualiza os campos de backup bkpCodProduto = codProduto End If Exit_incluir: Exit Function Err_incluir: incluir = False GoTo Exit_incluir End Function 'Mtodo Excluir [Com conhecimento de SQL] 'Exclui o objeto atual na tabela correspondente dentro do Banco de dados Function excluir() As Boolean On Error GoTo Err_excluir Dim objCon As New aclConexaoBD Dim strSql As String strSql = "Delete From Produto " & _ "Where codProduto = " & objCon.valorSql(codProduto) excluir = (objCon.executa(strSql) > 0) Exit_excluir: Exit Function Err_excluir: excluir = False GoTo Exit_excluir End Function 'Mtodo Obter [Com conhecimento de SQL] 'Recupera o objeto Produto atravs dos argumentos informados Function obter(argCodProduto As Variant) As Boolean On Error GoTo Err_obter Dim objCon As New aclConexaoBD Dim rstObter As Recordset Dim strSql As String strSql = "Select * " & _ "From Produto " & _ "Where codProduto = " & objCon.valorSql(argCodProduto) Set rstObter = objCon.consulta(strSql) If rstObter.RecordCount = 0 Then

59

obter = False Exit Function End If 'Atualiza os campos de backup e os identificadores codProduto = argCodProduto bkpCodProduto = argCodProduto 'Atualiza os campos restantes descricao = rstObter.Fields("descricao") estoqueMinimo = rstObter.Fields("estoqueMinimo") qtdEstoque = rstObter.Fields("qtdEstoque") unidade = rstObter.Fields("unidade") valorUnitario = rstObter.Fields("valorUnitario") obter = True 'Fecha o Recordset obter rstObter.close Exit_obter: Set rstObter = Nothing Exit Function Err_obter: obter = False GoTo Exit_obter End Function 'Mtodo Salvar [Com conhecimento de SQL] 'Salva o objeto atual na tabela correspondente dentro do Banco de dados Function salvar() As Boolean On Error GoTo Err_salvar Dim objCon As New aclConexaoBD Dim strSql As String If existe(bkpCodProduto) Then strSql = "Update Produto " & _ "Set codProduto = " & objCon.valorSql(codProduto) & _ ", descricao = " & objCon.valorSql(descricao) & _ ", estoqueMinimo = " & objCon.valorSql(estoqueMinimo) & _ ", qtdEstoque = " & objCon.valorSql(qtdEstoque) & _ ", unidade = " & objCon.valorSql(unidade) & _ ", valorUnitario = " & objCon.valorSql(valorUnitario) & _ " Where codProduto = " & objCon.valorSql(bkpCodProduto) salvar = (objCon.executa(strSql) > 0) Else salvar = incluir End If If salvar Then 'Atualiza as variveis de backup com o novo valor da chave bkpCodProduto = codProduto End If

60

Exit_salvar: Exit Function Err_salvar: salvar = False GoTo Exit_salvar End Function 'Mtodo baixarEstoque() 'Atualiza o estoque diminuindo a quantidade informada como parmetro 'e devolve um valor booleano que indica o sucesso da operao; Function baixarEstoque(argQtd As Double) As Boolean If dblQtdEstoque - Abs(argQtd) < 0 Then baixarEstoque = False Else dblQtdEstoque = dblQtdEstoque - Abs(argQtd) baixarEstoque = salvar End If End Function 'Mtodo subirEstoque() 'Atualiza o estoque acrescentando a quantidade informada como 'parmetro e devolve um valor booleano que indica o sucesso 'da operao; Function subirEstoque(argQtd As Double) As Boolean dblQtdEstoque = dblQtdEstoque + Abs(argQtd) subirEstoque = salvar End Function 'Mtodo estoqueBaixo 'Verifica o estoque atual e compara com o valor de estoque mnimo 'cadastrado para produto e devolve um valor booleano que indica 'se o valor atual est abaixo do previsto. Function estoqueBaixo() As Boolean If dblQtdEstoque < dblEstoqueMinimo Then estoqueBaixo = True End If End Function 'Fim da classe...

Pontos Importantes Desta vez, complementando os itens tratados no artigo anterior, vamos destacar o pontos a seguir. 1. Controle do estoque: Ao baixarmos o estoque de um produto, devemos verificar se possvel diminuir a quantidade informada, pois seria impossvel retirar mais do que a quantidade existente em estoque. Alm disso, no podemos baixar quantidades negativas de produto, pois isto significa que estaramos aumentando a quantidade ao invs de diminuir. O mtodo somente retornar True se for possvel realizar a operao.
61

Function baixarEstoque(argQtd As Double) As Boolean If dblQtdEstoque - Abs(argQtd) <= 0 Then baixarEstoque = False Else dblQtdEstoque = dblQtdEstoque - Abs(argQtd) baixarEstoque = salvar End If End Function

2. Converso de dados no momento da atribuio do valor: Para a atribuio de algumas propriedades ser realizada uma converso dos dados. No caso da unidade o valor ser convertido em letras maisculas para que fique padronizado. Para os atributos que armazenas a quantidade de produto em estoque e estoque mnimo no sero aceitos valores negativos, sendo assim todas as quantidades sero convertidas para seus respectivos valores absolutos.
Property Let estoqueMinimo(argEstoqueMinimo As Variant) dblEstoqueMinimo = Abs(argEstoqueMinimo) End Property Property Let qtdEstoque(argQtdEstoque As Variant) dblQtdEstoque = Abs(argQtdEstoque) End Property Property Let unidade(argUnidade As Variant) strUnidade = UCase(argUnidade) End Property

A Interface Grfica Assim como no artigo anterior, para que possamos visualizar e manipular os dados dos produtos devemos criar um formulrio com campos para preenchimento e botes de comando que coordenem as aes sobre os dados. A seguir sero definidas as caractersticas para o formulrio no qual iremos trabalhar com os dados dos objetos relativos aos produtos. Neste caso as especificaes de design tambm so apenas sugestes, porm as informaes de origem de dados, bloqueio de campos e formatos de dados so obrigatrias, sob pena de no obter a funcionalidade desejada. Estas especificaes estaro marcadas com um asterisco vermelho ao lado. No formulrio teremos uma lista contendo os produtos cadastrados, um campo de texto para realizar buscas, alguns campos de texto para digitao das informaes e os botes de comando para criar, salvar e excluir registros.

62

Obs: Somente as propriedades que foram alteradas para um valor diferente do padro sero apresentadas. Para as demais utilize o padro do sistema ou o valor que desejar, desde que no comprometa a funcionalidade do sistema. Formulrio
Nome: FProduto Largura: 19cm Altura: Cabealho (1cm) / Detalhe (10cm) Legenda: Sistema de Vendas Estilo da borda: fino Seletores de registro: No Botes de navegao: No Linhas divisrias: No Barras de rolagem: Nenhuma Caixa de controle: Sim Boto Fechar: Sim Botes Min Max: Nenhum Popup: Sim Janela Restrita: No

Controle Listbox
Nome: lstProduto * Origem da linha: SELECT Produto.codProduto, Produto.descricao, Produto.unidade, Produto.valorUnitario, Produto.estoqueMinimo, Produto.qtdEstoque FROM Produto ORDER BY Produto.descricao; * Tipo de Origem da Linha: Tabela/Consulta * Coluna acoplada: 1 * Nmero de colunas: 6 * Largura das colunas: 0cm;5cm;1cm;2cm;1cm;1cm * Largura: 10cm Cor do fundo: Cinza claro

Controles Textbox
Nome: txtPesquisa * Cor do fundo: Amarelo Alinhamento: Esquerda Nome: txtCodigo * Ativado: No * Bloqueado: Sim * Cor do fundo: Cinza Alinhamento: Centro Nome: txtDescricao * Cor do fundo: Azul claro Alinhamento: Esquerda Nome: txtUnidade * Cor do fundo: Azul claro Alinhamento: Esquerda Nome: txtValorUnitario * Cor do fundo: Azul claro Alinhamento: Direita

63

Formato: Unidade Monetria Nome: txtQtdEstoque * Cor do fundo: Azul claro Alinhamento: Centro Formato: 00 Nome: txtEstoqueMinimo * Cor do fundo: Cinza Alinhamento: Centro Formato: 00

Botes de Comando
Nome: btnNovo * Legenda: Novo Cor da Fonte: Azul escuro Nome: btnSalvar * Legenda: Salvar Cor da Fonte: Azul escuro Nome: btnExcluir * Legenda: Excluir Cor da Fonte: Vermelho escuro

Como sugesto de design os campos devero estar posicionados conforme demonstrado na figura a seguir, assim como devem ser adicionadas as legendas dos campos, as quais no foram descritas anteriormente:

64

Cdigos do Formulrio Para que nosso formulrio seja capaz de manipular os dados dos objetos produto deveremos implementar as funcionalidades necessrias que permitam criar um novo produto, buscar um produto j cadastrado, editar seus dados, salv-lo ou exclu-lo. Sendo assim criaremos algumas funes genricas que sejam teis para quantos procedimentos delas necessitem, reaplicando o conceito da reutilizao de cdigo. Alm disso incluiremos os cdigos dos campos que possuem eventos, alm dos botes de comando. Todos os cdigos a seguir devero ser colocados no mdulo do formulrio FProduto. 1. Pesquisa de produtos: Para facilitar a busca de produtos por qualquer parte do nome utilizaremos um campo de texto cujo valor ser o elemento condicional para o cdigo SQL que ser atribudo ao controle lstProduto, atualizando sua propriedade Origem da Linha aps cada alterao realizada no campo de pesquisa. Para isto devemos colocar o cdigo no evento Ao Alterar do campo txtPesquisa.
Private Sub txtPesquisa_Change() lstProduto.RowSource = "SELECT codProduto, descricao, " & _ "unidade, valorUnitario, estoqueMinimo, qtdEstoque " & _ "FROM Produto " & _ "WHERE descricao Like '*" & txtPesquisa.Text & "*' " & _ "ORDER BY Produto.descricao;" lstProduto.Requery End Sub

2. Limpeza dos campos: Para efetuar a limpeza dos campos de preenchimento utilizaremos um procedimento genrico cuja funo ser a de atribuir o valor nulo a todos os campos de texto e enviar o foco ao campo txtDescricao.
Private Sub limpaCampos() txtCodigo = Null txtDescricao = Null txtUnidade = Null txtValorUnitario = Null txtQtdEstoque = Null txtEstoqueMinimo = Null txtDescricao.SetFocus End Sub

3. Novo registro: Para a criao de um novo registro em branco simplesmente iremos chamar o procedimento limpaCampos() no evento Ao Clicar do boto btnNovo.
Private Sub btnNovo_Click() Call limpaCampos End Sub

65

4. Atualizao de campos: Assim como fizemos um procedimento genrico que limpa os campos para a criao de um novo registro teremos tambm um que preencha os campos com as informaes de um objeto produto passadas como parmetro, ou seja, o procedimento recebe um objeto completo e lana os valores de seus atributos nos campos do formulrio.
Private Sub atualizaCampos(argProduto As clsProduto) txtCodigo = argProduto.codProduto txtDescricao = argProduto.descricao txtUnidade = argProduto.unidade txtValorUnitario = argProduto.valorUnitario txtQtdEstoque = argProduto.qtdEstoque txtEstoqueMinimo = argProduto.estoqueMinimo End Sub

5. Escolha de produto: J que teremos tambm que trabalhar com produtos j cadastrados, devemos implementar alguma maneira de recuperar suas informaes. Faremos isto utilizando a Listbox lstProduto, que utilizar o procedimento atualizaCampos() para preencher os campos com as informaes do objeto do produto que foi clicado.
Private Sub lstProduto_Click() Dim objProduto As New clsProduto Dim codigoProduto As Long codigoProduto = lstProduto.Value If objProduto.obter(codigoProduto) Then Call atualizaCampos(objProduto) End If End Sub

6. Montagem de um objeto: Esta tambm uma funo genrica que ser utilizada por outros procedimentos ou funes. Seu objetivo criar um novo objeto produto, coletar os dados digitados nos campos, atribuir ao objeto criado, e devolv-lo para quem chamou a funo.
Private Function buscaCampos() As clsProduto Set buscaCampos = New clsProduto If IsNull(txtCodigo) Then txtCodigo = proximoCodigo("codProduto", "Produto") End If buscaCampos.codProduto = txtCodigo buscaCampos.descricao = txtDescricao buscaCampos.unidade = txtUnidade buscaCampos.valorUnitario = txtValorUnitario buscaCampos.qtdEstoque = txtQtdEstoque buscaCampos.estoqueMinimo = txtEstoqueMinimo End Function

66

7. Salvando um produto: Depois de informados os dados de um novo produto, ou alterados os dados de um produto j cadastrado, necessitaremos de um procedimento que efetue a gravao destes dados. Para isto o boto btnSalvar conter o cdigo necessrio no evento Ao Clicar. Ele far uso da funo buscaCampos(), a qual montar o objeto a ser salvo, e tambm o mtodo salvar() do objeto, apresentando uma mensagem ao usurio com o resultado da operao. Aps salvar os dados de um produto tambm deveremos atualizar a Listbox para que apresente estes dados, sejam eles novos ou apenas alterados.
Private Sub btnSalvar_Click() Dim objProduto As clsProduto If Not IsNull(txtDescricao) And Not IsNull(txtUnidade) And _ Not IsNull(txtValorUnitario) Then Set objProduto = buscaCampos If objProduto.salvar Then MsgBox "O produto foi salvo com sucesso.",vbInformation,"Salvar produto" lstProduto.Requery Call atualizaCampos(objProduto) Else MsgBox "Ocorreu um erro durante o salvamento.", vbExclamation, _ "Salvar produto" End If Else MsgBox "Informe os dados do produto.", vbExclamation, "Salvar produto" End If End Sub

8. Excluindo um produto: Alm de criar e alterar os dados de um produto, importante que tambm possamos exclu-lo definitivamente de nosso cadastro. Logo devemos tambm implementar a funcionalidade para o boto btnExcluir, que conter o cdigo necessrio tambm no evento Ao Clicar. Ele tambm dever fazer uso da funo buscaCampos() para a montagem do objeto a ser excludo, pois caso contrrio a classe no saber que objeto deve ser excludo. Em seguida utilizaremos o mtodo excluir() do objeto, apresentando uma mensagem ao usurio com o resultado da operao. claro que aps excluir um produto tambm deveremos atualizar a Listbox para que a mesma reflita as alteraes realizadas.
Private Sub btnExcluir_Click() Dim objProduto As clsProduto If Not IsNull(txtCodigo) Then If MsgBox("Confirma a excluso do registro?", vbQuestion + vbYesNo, _ "Excluir produto") = vbYes Then Set objProduto = buscaCampos If objProduto.excluir Then

67

MsgBox "O registro foi excludo com sucesso.", vbInformation, _ "Excluir produto" lstProduto.Requery Call limpaCampos Else MsgBox "Ocorreu um erro durante a excluso.", vbExclamation, _ "Excluir produto" End If End If End If End Sub

Sistema de Exemplo Novamente ser disponibilizado o link para download do sistema de vendas, j atualizado com todos os recursos do estado de desenvolvimento em que se encontrar o projeto. Mais uma vez recomendo aos desenvolvedores que o consultem apenas para tirar dvidas e realizar comparaes. Aqueles que realmente quiserem aprender faam toda a codificao e a montagem das telas manualmente. Somente assim a sua mente ir se defrontar com as dificuldades do processo e assimilar os passos necessrios para a resoluo dos problemas e a concluso dos objetivos. Segue o link para download:
Venda00.zip

Caso ainda no esteja com uma tela principal abra o formulrio FProduto diretamente na janela Banco de Dados (Access 2003-) ou no Painel de Navegao (Access 2007+). Concluso Nesta fase no encontramos praticamente nada de novo, mas tivemos a chance de relembrar a maioria dos procedimentos vistos at o momento. A codificao das funcionalidades da classe Produto so muito semelhantes codificao da classe Cliente. Porm este era o nosso objetivo: relembrar o passo-a-passo necessrio para utilizar objetos em sistemas informatizados no MS-Access/VBA. Com relao parte realmente prtica da srie de artigos estamos chegando na reta final. No prximo artigo, que ser o ltimo em que trabalharemos com a programao de classes, poderemos perceber um pouco mais da dinmica de troca de informaes entre os objetos de um sistema. Ao realizarmos vendas de produtos para clientes estaremos englobando a utilizao de todas as nossas classes de uma s vez, trabalhando com vrios objetos instanciados simultaneamente para a concluso das tarefas. Com toda certeza voc, caro leitor que nos acompanha, j deve estar imaginando os objetos se interagindo, os mtodos realizando tarefas e retornando valores, os atributos

68

recebendo e fornecendo valores, os objetos sendo enviados inteiramente para mtodos de outros objetos, e assim sucessivamente. isto a, orgulhe-se de dizer: Agora estou quase dominando essa tal de OO!

69

Classes VII - As Classes Venda e DetalheVenda


Estamos chegando na reta final da criao do sistema de vendas orientado a objetos. Nesta etapa veremos a codificao das classes Venda e DetalheVenda, alm da implementao de um PDV que ser a nossa interface grfica para realizao das vendas. No desenrolar da criao dos cdigos com certeza voc encontrar rotinas repetitivas, funes no to necessrias e instncias em excesso. Alm disso, poder sentir falta de funcionalidades importantes em um PDV ou mesmo na totalidade do sistema de vendas. E digo mais, agora que j praticamente um expert em VBA e orientao a objetos perceber que um ou outro mtodo poderia ser mais bem implementado de outra maneira. Mas antes de queimar neurnios tentando entender por que o trabalho est assim to mal projetado, lembre-se que tudo foi feito com fins didticos, e a repetitividade foi proposital, somente para que voc fique cansado de tanto manipular objetos e realmente aprenda como utiliz-los.

No estaremos totalmente livres do aparecimento de bugs, pois apenas nos locais estritamente necessrios foram inseridas rotinas de tratamento de erros. Estes algoritmos so cansativos de se preparar, ento me dei ao luxo de no incluir em todos os procedimentos, funes e mtodos do sistema. Porm nada impede que voc, que j pesquisou o assunto e agora saca muito de programao, implemente ao seu bel prazer, corrigindo erros que porventura se apresentem. Ento chega de papo e mos obra... A Consulta CVenda Deveremos criar uma consulta que ser utilizada por um mtodo da classe Venda. O nome da consulta ser CVenda e seu cdigo SQL ser o seguinte:
SELECT Venda.codVenda, Venda.codCliente, Venda.dataVenda, DetalheVenda.codProduto, DetalheVenda.qtdProduto, Produto.descricao, Produto.unidade, Produto.valorUnitario, (Produto.valorUnitario*DetalheVenda.qtdProduto) AS SubTotal FROM Venda LEFT JOIN (Produto RIGHT JOIN DetalheVenda ON Produto.codProduto=DetalheVenda.codProduto) ON Venda.codVenda=DetalheVenda.codVenda;

A Classe Venda O objetivo desta classe, conforme dito anteriormente, oferecer funcionalidades de incluso, consulta, atualizao e excluso dos objetos do tipo venda. Este o cdigo da classe, com todos os seus atributos e mtodos, que se utilizam de funes prprias, bem como dos mtodos dos objetos da classe ConexaoBD e da classe Cliente.
70

Cdigo da classe:
Option Compare Database Option Explicit 'Atributos da Classe 'Atributo de backup e atributo identificador da Classe 'PK - Cdigo que identifica a venda. Private bkpCodVenda As Variant Private lngCodVenda As Variant 'FK - Cdigo que indica o cliente relativo venda. Private lngCodCliente As Variant 'Objeto da classe de Descrio de Tipo Private objetoCliente As New clsCliente 'Data de realizao da venda. Private dtmDataVenda As Variant 'Mtodos Get, Set e Let da Classe Property Get codVenda() As Variant codVenda = lngCodVenda End Property Property Let codVenda(argCodVenda As Variant) lngCodVenda = argCodVenda If IsEmpty(bkpCodVenda) Then bkpCodVenda = lngCodVenda End If End Property Property Get codCliente() As Variant codCliente = lngCodCliente End Property Property Let codCliente(argCodCliente As Variant) lngCodCliente = argCodCliente End Property Property Get dataVenda() As Variant dataVenda = dtmDataVenda End Property Property Let dataVenda(argDataVenda As Variant) dtmDataVenda = argDataVenda End Property Property Get objCliente() As Variant If Not IsEmpty(codCliente) Then If objetoCliente.obter(codCliente) Then Set objCliente = objetoCliente End If End If End Property 'Mtodo Existe [Com conhecimento de SQL] 'Verifica a existncia do objeto Venda na tabela

71

'correspondente no Banco de Dados Function existe(argCodVenda As Variant) As Boolean On Error GoTo Err_existe Dim objCon As New aclConexaoBD Dim rstExiste As Recordset Dim strSql As String existe = False strSql = "Select * " & _ "From Venda " & _ "Where codVenda = " & objCon.valorSql(argCodVenda) Set rstExiste = objCon.consulta(strSql) If rstExiste.RecordCount > 0 Then existe = True End If 'Fecha o Recordset existe rstExiste.close Exit_existe: Set rstExiste = Nothing Exit Function Err_existe: existe = False GoTo Exit_existe End Function 'Mtodo Incluir [Com conhecimento de SQL] 'Inclui um novo objeto na tabela correspondente 'dentro do Banco de dados Function incluir() As Boolean On Error GoTo Err_incluir Dim objCon As New aclConexaoBD Dim strSql As String strSql = "Insert Into " & _ "Venda(codVenda,codCliente,dataVenda) " & _ "Values(" & objCon.valorSql(codVenda) & "," & _ objCon.valorSql(codCliente) & "," & _ objCon.valorSql(dataVenda) & ")" incluir = (objCon.executa(strSql) > 0) If incluir Then 'Atualiza os campos de backup bkpCodVenda = codVenda End If Exit_incluir: Exit Function Err_incluir: incluir = False GoTo Exit_incluir

72

End Function 'Mtodo Excluir [Com conhecimento de SQL] 'Exclui o objeto atual na tabela correspondente 'dentro do Banco de dados Function excluir() As Boolean On Error GoTo Err_excluir Dim objCon As New aclConexaoBD Dim strSql As String strSql = "Delete From Venda " & _ "Where codVenda = " & objCon.valorSql(codVenda) excluir = (objCon.executa(strSql) > 0) Exit_excluir: Exit Function Err_excluir: excluir = False GoTo Exit_excluir End Function 'Mtodo Obter [Com conhecimento de SQL] 'Recupera o objeto Venda atravs dos argumentos informados Function obter(argCodVenda As Variant) As Boolean On Error GoTo Err_obter Dim objCon As New aclConexaoBD Dim rstObter As Recordset Dim strSql As String strSql = "Select * " & _ "From Venda " & _ "Where codVenda = " & objCon.valorSql(argCodVenda) Set rstObter = objCon.consulta(strSql) If rstObter.RecordCount = 0 Then obter = False Exit Function End If 'Atualiza os campos de backup 'e os identificadores codVenda = argCodVenda bkpCodVenda = argCodVenda 'Atualiza os campos restantes codCliente = rstObter.Fields("codCliente") dataVenda = rstObter.Fields("dataVenda") obter = True 'Fecha o Recordset obter rstObter.close Exit_obter:

73

Set rstObter = Nothing Exit Function Err_obter: obter = False GoTo Exit_obter End Function 'Mtodo Salvar [Com conhecimento de SQL] 'Salva o objeto atual na tabela correspondente 'dentro do Banco de dados Function salvar() As Boolean On Error GoTo Err_salvar Dim objCon As New aclConexaoBD Dim strSql As String If existe(bkpCodVenda) Then strSql = "Update Venda " & _ "Set codVenda = " & objCon.valorSql(codVenda) & _ ", codCliente = " & objCon.valorSql(codCliente) & _ ", dataVenda = " & objCon.valorSql(dataVenda) & _ " Where codVenda = " & objCon.valorSql(bkpCodVenda) salvar = (objCon.executa(strSql) > 0) Else salvar = incluir End If If salvar Then 'Atualiza as variveis de backup 'com o novo valor da chave bkpCodVenda = codVenda End If Exit_salvar: Exit Function Err_salvar: salvar = False GoTo Exit_salvar End Function 'Mtodo getValorTotal 'Calcula a valor total da venda atual e 'devolve o valor como resultado. Function getValorTotal() As Currency getValorTotal = Nz(DSum("SubTotal", "CVenda", _ "codVenda=" & Nz(lngCodVenda, -1))) End Function 'Fim da classe...

A Classe DetalheVenda

74

O objetivo desta classe, conforme dito anteriormente, oferecer funcionalidades de consulta e atualizao dos objetos de ligao entre a venda e os produtos componentes. Este o cdigo da classe, com todos os seus atributos e mtodos, que se utilizam de funes prprias, bem como dos mtodos dos objetos da classe ConexaoBD e da classe Produto. Cdigo da classe:
Option Compare Database Option Explicit 'Atributos da Classe 'Atributo de backup e 'PK - FK - Cdigo que Private bkpCodProduto Private lngCodProduto atributo identificador da Classe indica o produto relacionado. As Variant As Variant

'Atributo de backup e atributo identificador da Classe 'PK - FK - Cdigo que indica a venda relacionada. Private bkpCodVenda As Variant Private lngCodVenda As Variant 'Quantidade do poduto relacionado. Private dblQtdProduto As Variant 'Mtodos Get, Set e Let da Classe Property Get codProduto() As Variant codProduto = lngCodProduto End Property Property Let codProduto(argCodProduto As Variant) lngCodProduto = argCodProduto If IsEmpty(bkpCodProduto) Then bkpCodProduto = lngCodProduto End If End Property Property Get codVenda() As Variant codVenda = lngCodVenda End Property Property Let codVenda(argCodVenda As Variant) lngCodVenda = argCodVenda If IsEmpty(bkpCodVenda) Then bkpCodVenda = lngCodVenda End If End Property Property Get qtdProduto() As Variant qtdProduto = dblQtdProduto End Property Property Let qtdProduto(argQtdProduto As Variant) dblQtdProduto = argQtdProduto

75

End Property 'Mtodo Existe [Com conhecimento de SQL] 'Verifica a existncia do objeto DetalheVenda na 'tabela correspondente no Banco de Dados Function existe(argCodProduto As Variant, _ argCodVenda As Variant) As Boolean On Error GoTo Err_existe Dim objCon As New aclConexaoBD Dim rstExiste As Recordset Dim strSql As String existe = False strSql = "Select * " & _ "From DetalheVenda " & _ "Where codProduto = " & objCon.valorSql(argCodProduto) _ And codVenda = " & objCon.valorSql(argCodVenda)" Set rstExiste = objCon.consulta(strSql) If rstExiste.RecordCount > 0 Then existe = True End If 'Fecha o Recordset existe rstExiste.close Exit_existe: Set rstExiste = Nothing Exit Function Err_existe: existe = False GoTo Exit_existe End Function 'Mtodo Incluir [Com conhecimento de SQL] 'Inclui um novo objeto na tabela correspondente 'dentro do Banco de dados Function incluir() As Boolean On Error GoTo Err_incluir Dim objCon As New aclConexaoBD Dim strSql As String strSql = "Insert Into " & _ "DetalheVenda(codProduto,codVenda,qtdProduto) " & _ "Values(" & objCon.valorSql(codProduto) & "," & _ objCon.valorSql(codVenda) & "," & _ objCon.valorSql(qtdProduto) & ")" incluir = (objCon.executa(strSql) > 0) If incluir Then 'Atualiza os campos de backup bkpCodProduto = codProduto bkpCodVenda = codVenda End If

76

Exit_incluir: Exit Function Err_incluir: incluir = False GoTo Exit_incluir End Function 'Mtodo Excluir [Com conhecimento de SQL] 'Exclui o objeto atual na tabela correspondente 'dentro do Banco de dados Function excluir() As Boolean On Error GoTo Err_excluir Dim objCon As New aclConexaoBD Dim strSql As String strSql = "Delete From DetalheVenda " & _ "Where codProduto = " & objCon.valorSql(codProduto) & _ " and codVenda = " & objCon.valorSql(codVenda) excluir = (objCon.executa(strSql) > 0) Exit_excluir: Exit Function Err_excluir: excluir = False GoTo Exit_excluir End Function 'Mtodo Obter [Com conhecimento de SQL] 'Recupera o objeto DetalheVenda atravs dos argumentos informados Function obter(argCodProduto As Variant, _ argCodVenda As Variant) As Boolean On Error GoTo Err_obter Dim objCon As New aclConexaoBD Dim rstObter As Recordset Dim strSql As String strSql = "Select * " & _ "From DetalheVenda " & _ "Where codProduto = " & objCon.valorSql(argCodProduto) & _ " And codVenda = " & objCon.valorSql(argCodVenda) Set rstObter = objCon.consulta(strSql) If rstObter.RecordCount = 0 Then obter = False Exit Function End If 'Atualiza os campos de backup 'e os identificadores codProduto = argCodProduto bkpCodProduto = argCodProduto codVenda = argCodVenda

77

bkpCodVenda = argCodVenda 'Atualiza os campos restantes qtdProduto = rstObter.Fields("qtdProduto") obter = True 'Fecha o Recordset obter rstObter.close Exit_obter: Set rstObter = Nothing Exit Function Err_obter: obter = False GoTo Exit_obter End Function 'Mtodo Salvar [Com conhecimento de SQL] 'Salva o objeto atual na tabela correspondente 'dentro do Banco de dados Function salvar() As Boolean On Error GoTo Err_salvar Dim objCon As New aclConexaoBD Dim strSql As String If existe(bkpCodProduto, bkpCodVenda) Then strSql = "Update DetalheVenda " & _ "Set codProduto = " & objCon.valorSql(codProduto) & _ ", codVenda = " & objCon.valorSql(codVenda) & _ ", qtdProduto = " & objCon.valorSql(qtdProduto) & _ " Where codProduto = " & objCon.valorSql(bkpCodProduto) & _ " and codVenda = " & objCon.valorSql(bkpCodVenda) salvar = (objCon.executa(strSql) > 0) Else salvar = incluir End If If salvar Then 'Atualiza as variveis de backup 'com o novo valor da chave bkpCodProduto = codProduto bkpCodVenda = codVenda End If Exit_salvar: Exit Function Err_salvar: salvar = False GoTo Exit_salvar End Function 'Mtodo getSubTotal 'Calcula o subtotal do produto vendido para a

78

'venda atual e devolve o valor como resultado Function getSubTotal() As Currency Dim objProduto As New clsProduto If Not IsNull(lngCodProduto) And Not IsNull(dblQtdProduto) Then If objProduto.obter(lngCodProduto) Then getSubTotal = objProduto.valorUnitario * dblQtdProduto End If End If End Function 'Mtodo getListaProduto(argCodVenda As Long) 'Recebe como parmetro um cdigo de uma venda e devolve 'um conjunto de registros contendo os cdigos dos produtos, 'alm de outros dados, relativos venda informada. Function getListaProduto(argCodVenda As Long) As Recordset Dim strSql As String Dim objCon As New aclConexaoBD strSql = "SELECT Venda.codVenda, Venda.codCliente, Venda.dataVenda, " & _ "DetalheVenda.codProduto, DetalheVenda.qtdProduto, " & _ "Produto.descricao, Produto.unidade, Produto.valorUnitario, " & _ "(Produto.valorUnitario * DetalheVenda.qtdProduto) AS SubTotal " & _ "FROM Venda LEFT JOIN (Produto RIGHT JOIN DetalheVenda ON " & _ "Produto.codProduto=DetalheVenda.codProduto) " & _ "ON Venda.codVenda=DetalheVenda.codVenda " & _ "Where Venda.codVenda=" & argCodVenda Set getListaProduto = objCon.consulta(strSql) End Function 'Fim da classe...

Mais Funes Auxiliares Para exibir os itens vendidos na tela, no formato de um cupom fiscal, necessitaremos de uma funo que busque os ltimos itens registrados na venda para que possamos apresent-los ao usurio. Assim sendo deveremos incluir mais uma funo no mdulo Funcoes criado anteriormente, que ser chamada de listaProdutos() e receber como parmetros o cdigo da venda e o nmero de itens, a partir do ltimo, que devem ser retornados.
Function listaProdutos(argCodVenda As Long, _ Optional ultimos As Integer = 0) As String Dim rstLista As Recordset Dim objDetalheVenda As New clsDetalheVenda Dim contador

79

Set rstLista = objDetalheVenda.getListaProduto(argCodVenda) If Not rstLista.EOF Then rstLista.MoveLast Else listaProdutos = "" Exit Function End If While Not rstLista.BOF And (contador < ultimos Or ultimos = 0) listaProdutos = " 3) & _ " _ " " & FormatCurrency(rstLista("SubTotal")) & _ vbCrLf & listaProdutos listaProdutos = Format(rstLista("codProduto"), "000000") & " " & _ rstLista("unidade") & " rstLista("descricao") & _ vbCrLf & listaProdutos rstLista.MovePrevious contador = contador + 1 Wend End Function " & x " & FormatCurrency(rstLista("valorUnitario")) & " & FormatNumber(rstLista("qtdProduto"),

Repare que a funo busca os itens comeando do final do conjunto de registros, inclusive montando as linhas tambm do final para o incio, at que seja completada a quantidade de itens solicitada, ou todos os itens caso a opo no seja informada. Ponto Importante Desta vez, relembrando e aprimorando os conceitos da orientao a objetos, vamos destacar o ponto a seguir. 1. Atributo do tipo objeto: Nos atributos da classe Venda podemos encontrar um que faz referncia classe Cliente, ou seja, o atributo um objeto da classe Cliente. Por que isto? Apenas adiantando um pouco o assunto do prximo artigo criaremos um modelo de cupom fiscal (bem simples, lgico!!!) no qual esta opo ser muito til. Ao invs de criarmos uma varivel de objeto da classe Cliente e a instanciarmos bastar utilizarmos o atributo includo na classe Venda depois que a mesma for obtida atravs do cdigo da venda. Neste caso o objeto cliente ser instanciado dentro da classe Venda automaticamente assim que o atributo for referenciado. Com isso ser possvel tambm acessar todos os atributos e mtodos do objeto cliente. Veja os trechos de cdigo, primeiro o mtodo de acesso ao atributo e depois a montagem do cupom fiscal: Declarao da varivel e mtodo de acesso ao atributo:
'Objeto da classe de Descrio de Tipo Private objetoCliente As New clsCliente

80

... Property Get objCliente() As Variant If Not IsEmpty(codCliente) Then If objetoCliente.obter(codCliente) Then Set objCliente = objetoCliente End If End If End Property ...

Montagem do Cupom Fiscal:


cupomVenda = cupomVenda & "CPF: " & objVenda.objCliente.cpf & vbCrLf cupomVenda = cupomVenda & "Nome: " & objVenda.objCliente.nomeCliente & vbCrLf

A Interface Grfica Desta vez para que possamos visualizar e manipular os dados das vendas resolvi criar um interessante sistema de PDV para ser a nossa interface grfica, j que h tanta curiosidade acerca dos mesmos. A tela contar com exibio dos itens registrados em formato de cupom, alm do clculo e registro de produtos utilizando o esquema de digitao do cdigo multiplicado pela quantidade em um nico campo. Tambm faremos a verificao da descrio do produto a partir do cdigo e o clculo de troco a partir do valor pago, alm de outras pequenas funes. Como no poderia faltar faremos o controle de estoque, tanto baixando itens vendidos como subindo produtos devolvidos, alm de verificao de estoque baixo. A seguir sero definidas as caractersticas para o PDV. Assim como nos anteriores as especificaes de design tambm so apenas sugestes, porm as informaes de origem de dados, bloqueio de campos e formatos de dados so obrigatrias, sob pena de, novamente, no obter a funcionalidade desejada. Como de praxe as especificaes estaro marcadas com um asterisco vermelho ao lado. No formulrio teremos um rtulo que exibir os produtos vendidos, alguns campos de texto para digitao das informaes e os botes de comando para criar e cancelar vendas, excluir produtos e fechar a tela do PDV. Obs: Somente as propriedades que foram alteradas para um valor diferente do padro sero apresentadas. Para as demais utilize o padro do sistema, ou o valor que desejar, desde que no comprometa a funcionalidade do sistema. Formulrio
Nome: FVenda Largura: 22cm Altura: Cabealho (1,6cm) / Detalhe (15cm)

81

Cor do fundo: Cabealho (Vermelho) / Detalhe (Azul escuro) Legenda: Sistema de Vendas Estilo da borda: Fino Seletores de registro: No Botes de navegao: No Linhas divisrias: No Barras de rolagem: Nenhuma Caixa de controle: No Boto Fechar: No Botes Min Max: Nenhum Popup: Sim Janela Restrita: No

Controle Label
Nome: lblProdutos * Largura: 9,7cm * Altura: 10,4cm *

Controles Textbox
Nome: txtCodigoVenda * Formato: 000000 Ativado: No * Bloqueado: Sim * Cor do fundo: Cinza Alinhamento: Centro Nome: txtData * Formato: dd/mm/yyyy * Cor do fundo: Branco Mscara de entrada: 00/00/00;;_ Alinhamento: Centro Nome: txtCodCliente * Formato: 000 Cor do fundo: Branco Alinhamento: Centro Nome: txtNomeCliente * Cor do fundo: Cinza Alinhamento: Esquerda Ativado: No * Bloqueado: Sim * Nome: txtProdutoQtd * Cor do fundo: Amarelo Alinhamento: Esquerda Tamanho da Fonte: 28 Nome: txtDescricao * Cor do fundo: Cinza Alinhamento: Esquerda Tamanho da Fonte: 14 Ativado: No * Bloqueado: Sim * Nome: txtUnidade * Cor do fundo: Cinza Alinhamento: Centro

82

Tamanho da Fonte: 14 Ativado: No * Bloqueado: Sim * Nome: txtQtd * Cor do fundo: Cinza Formato: Padro Casas decimais: 3 Alinhamento: Centro Tamanho da Fonte: 14 Ativado: No * Bloqueado: Sim * Nome: txtValorUnt * Cor do fundo: Cinza Formato: Unidade Monetria Alinhamento: Direita Tamanho da Fonte: 14 Ativado: No * Bloqueado: Sim * Nome: txtSubTotal * Cor do fundo: Cinza Formato: Unidade Monetria Alinhamento: Direita Tamanho da Fonte: 14 Ativado: No * Bloqueado: Sim * Nome: txtTotal * Cor do fundo: Laranja Formato: Unidade Monetria Alinhamento: Direita Tamanho da Fonte: 24 Ativado: No * Bloqueado: Sim * Nome: txtPago * Cor do fundo: Cinza Formato: Unidade Monetria Alinhamento: Direita Tamanho da Fonte: 16 Nome: txtTroco * Cor do fundo: Cinza Formato: Unidade Monetria Alinhamento: Direita Tamanho da Fonte: 16 Ativado: No * Bloqueado: Sim *

Botes de Comando
Nome: btnExcluirProduto * Legenda: Excluir Produto Cor da Fonte: Vermelho escuro Nome: btnCancelar * Legenda: Cancelar Venda Cor da Fonte: Vermelho escuro

83

Nome: btnNovo * Legenda: Nova Venda Cor da Fonte: Azul escuro Nome: btnFechar * Legenda: Fechar Cor da Fonte: Vermelho escuro

Como sugesto de design os campos devero estar posicionados conforme demonstrado na figura a seguir, assim como devem ser adicionadas as legendas dos campos, as quais no foram descritas anteriormente:

Cdigos do Formulrio Para que nosso formulrio seja capaz de manipular os dados dos objetos produto deveremos implementar as funcionalidades necessrias que permitam criar uma nova venda, calcular os valores dos produtos vendidos, registrar as vendas, controlar estoque, devolver produtos e cancelar a venda. Sendo assim criaremos algumas funes genricas que sejam teis para quantos procedimentos delas necessitem, reaplicando o conceito da reutilizao de cdigo. Alm disso, incluiremos os cdigos dos campos que possuem eventos, assim como dos botes de comando. Todos os cdigos a seguir devero ser colocados no mdulo do formulrio FVenda.
84

1. Limpeza dos campos: Para efetuar a limpeza dos campos de preenchimento utilizaremos dois procedimentos genrico cuja funo ser a de atribuir o valor nulo a todos os campos de texto, deixar o rtulo de exibio do cupom em branco e enviar o foco ao campo txtProdutoQtd. Repare que um procedimento reutiliza o cdigo do outro, j que os dois so independentes e o primeiro utilizado apenas na descrio do produto e o segundo limpa todos os campos da tela.
Private Sub limpaExibicaoProduto() txtDescricao = Null txtUnidade = Null txtQtd = Null txtValorUnt = Null txtSubtotal = Null End Sub Private Sub limpaCampos() txtCodigoVenda = Null txtData = Null txtCodCliente = Null txtNomeCliente = Null txtProdutoQtd = Null txtTotal = Null txtPago = Null txtTroco = Null Call limpaExibicaoProduto lblProdutos.Caption = "" txtProdutoQtd.SetFocus End Sub

2. Nova venda: Para a criao de uma nova venda em branco simplesmente iremos chamar o procedimento limpaCampos() no evento Ao Clicar do boto btnNovo.
Private Sub btnNovo_Click() Call limpaCampos End Sub

3. Clculo do troco: Com a finalidade de informar o valor do troco a partir do valor da venda e do valor pago utilizaremos um procedimento que ser acionado no evento Aps Atualizar do campo txtPago.
Private Sub txtPago_AfterUpdate() If Not IsNull(txtPago) Then txtTroco = CDbl(txtPago) - CDbl(Nz(txtTotal)) Else txtTroco = Null End If End Sub

4. Exibio da lista de produtos registrados: Para exibir a lista de produtos registrados sempre que for includo ou retirado algum produto utilizaremos um procedimento genrico cuja funo ser a de buscar o lista utilizando a funo listaProdutos(), atualizar a exibio do valor total da venda no campo txtTotal e acionar o procedimento que calcula e exibe o valor do troco.
Sub atualizaLista()

85

Dim objVenda As New clsVenda If Not IsNull(txtCodigoVenda) Then If objVenda.obter(txtCodigoVenda) Then lblProdutos.Caption = listaProdutos(Nz(txtCodigoVenda, -1), 10) txtTotal = objVenda.getValorTotal Call txtPago_AfterUpdate End If End If End Sub

5. Carregamento do formulrio: No evento Ao Carregar do formulrio implementaremos uma pequena rotina que simplesmente atualizar a lista de produtos e mover o foco para o campo txtProdutoQtd. A atualizao da lista s far sentido quando abrirmos novamente um formulrio j carregado anteriormente (por exemplo, quando abrimos o formulrio, mudamos para o modo Design e voltamos para o modo Formulrio).
Private Sub Form_Load() Call atualizaLista txtProdutoQtd.SetFocus End Sub

6. Criao da venda: Ao iniciarmos uma venda deveremos gerar o cdigo da mesma. Assim criaremos um procedimento genrico que lana mo da funo proximoCodigo() para gerar o cdigo da venda, alm de atribuir a data atual caso o usurio no tenha informado alguma data especfica.
Sub preencheVenda() If IsNull(txtCodigoVenda) Then txtCodigoVenda = proximoCodigo("codVenda", "Venda") End If If IsNull(txtData) Then txtData = Date End If End Sub

7. Busca do cliente: Para que seja exibido o nome do cliente assim que o cdigo for informado utilizaremos esta rotina no evento Aps Atualizar do campo txtCodCliente. Assim ser preenchido o campo txtNomeCliente a partir dos dados retornados pelo objeto cliente, ou ento um aviso ao usurio informando que o cdigo do cliente invlido. Ao ser informado um cliente vlido a venda tambm ser gerada atravs de uma chamada ao procedimento preencheVenda().
Private Sub txtCodCliente_AfterUpdate() Dim objCliente As New clsCliente If Not IsNull(txtCodCliente) Then If objCliente.obter(CLng(txtCodCliente)) Then Call preencheVenda txtNomeCliente = objCliente.nomeCliente

86

Else txtNomeCliente = Null txtCodCliente = Null MsgBox "Cdigo do cliente invlido!", _ vbExclamation, "Erro!" End If End If End Sub

8. Busca do produto em tempo real: O procedimento a seguir permitir que seja exibida a descrio e a unidade do produto no exato momento em que o cdigo for digitado. Ele ser includo no evento Ao Alterar do campo txtProdutoQtd. Caso o cdigo no corresponda a nenhum produto ser exibida uma mensagem no campo txtDescricao informando que o cdigo no est cadastrado. Veja que ele somente ser executado completamente caso o valor digitado possa ser interpretado como valor numrico, ou seja, um valor compatvel com o cdigo do produto.
Private Sub txtProdutoQtd_Change() Dim objProduto As New clsProduto If txtProdutoQtd.Text <> "" Then If InStr(txtProdutoQtd.Text, "*") = 0 Then If IsNumeric(txtProdutoQtd.Text) Then If objProduto.obter(CLng(txtProdutoQtd.Text)) Then txtDescricao = objProduto.descricao txtUnidade = objProduto.unidade Else txtDescricao = "Cdigo no cadastrado..." txtUnidade = Null End If txtQtd = Null txtValorUnt = Null txtSubtotal = Null End If End If Else txtDescricao = Null txtUnidade = Null End If End Sub

9. Exibio de produto: Para exibirmos todos os dados do produto a ser registrado em uma venda criaremos um procedimento genrico que recebe como argumento um objeto detalhe de venda cujas propriedades e mtodos fornecero as informaes para o preenchimento dos campos. Repare que o valor do subtotal calculado pelo mtodo getSubTotal() do objeto.
Sub exibeProduto(argDetalhe As clsDetalheVenda) Dim objProduto As New clsProduto If objProduto.obter(argDetalhe.codProduto) Then txtDescricao = objProduto.descricao txtUnidade = objProduto.unidade txtQtd = argDetalhe.qtdProduto

87

txtValorUnt = objProduto.valorUnitario txtSubtotal = argDetalhe.getSubTotal End If End Sub

10. Registro do produto: Cada produto constante da venda dever ser registrado antes do fechamento da venda. Para isto deveremos realizar vrias verificaes, utilizando vrios objetos neste procedimento que ser includo no evento Antes de Atualizar do campo txtProdutoQtd para que seja capaz de cancelar o registro caso alguma informao no esteja correta. Primeiro ser verificada a validade do cdigo do produto e a correta informao da quantidade, caso seja includa, atribuindo uma unidade em caso contrrio. Em seguida ser checada a validade do cdigo da venda e tambm da data, alm do cdigo do cliente, caso seja informado (uma venda sem cliente ser considerada venda ao consumidor). Para no ocorrerem conflitos o cdigo da venda ser sempre gerado automaticamente, no sendo permitida sua alterao posterior. O prximo passo ser verificar se a quantidade solicitada existe em estoque. Caso no possa ser atendida integralmente o registro cancelado. Existindo a quantidade o produto registrado, assim como qualquer alterao nos dados da venda. Por ltimo a exibio dos dados atualizada e o estoque atualizado, informando o usurio se o estoque ficar abaixo do mnimo. Caso ocorram erros durante o processo o esquema de tratamento de erros avisa o usurio sobre qual passo gerou o problema.
Private Sub txtProdutoQtd_BeforeUpdate(Cancel As Integer) On Error GoTo Err_txtProdutoQtd_AfterUpdate Dim Dim Dim Dim Dim Dim Dim codigoProduto As Long qtdProduto As Double posicao As Integer status As Integer objVenda As New clsVenda objDetalhe As New clsDetalheVenda objProduto As New clsProduto

status = 0 If Not IsNull(txtProdutoQtd) Then Call preencheVenda posicao = InStr(txtProdutoQtd, "*") If posicao > 0 Then status = 1 codigoProduto = CLng(Left(txtProdutoQtd, posicao - 1)) status = 2 qtdProduto = CDbl(Right(txtProdutoQtd, Len(txtProdutoQtd) posicao)) Else status = 3 codigoProduto = CLng(txtProdutoQtd) qtdProduto = 1 End If status = 4 objVenda.codVenda = CLng(txtCodigoVenda) status = 5

88

objVenda.dataVenda = CDate(txtData) status = 6 If Not IsNull(txtCodCliente) Then objVenda.codCliente = CLng(txtCodCliente) End If If objProduto.obter(codigoProduto) Then If objProduto.qtdEstoque < qtdProduto Then MsgBox "O estoque existente no suficiente!" & vbCrLf & vbLf & _ "Estoque atual: " & FormatNumber(objProduto.qtdEstoque, 3) & _ " " & objProduto.unidade, _ vbExclamation, "Estoque Baixo" Cancel = True Exit Sub End If End If status = 7 If objVenda.salvar Then objDetalhe.codVenda = objVenda.codVenda objDetalhe.codProduto = codigoProduto objDetalhe.qtdProduto = qtdProduto status = 8 If objDetalhe.salvar Then Call exibeProduto(objDetalhe) Call atualizaLista If objProduto.obter(codigoProduto) Then status = 9 If objProduto.baixarEstoque(qtdProduto) Then If objProduto.estoqueBaixo Then MsgBox "O estoque ficou abaixo da quantidade mnima!" & _ vbCrLf & vbLf & _ "Estoque atual: " & FormatNumber(objProduto.qtdEstoque, 3) & _ " " & objProduto.unidade, vbExclamation, "Estoque Baixo" End If Else MsgBox "Ocorreu um erro na atualizao do estoque.", _ vbExclamation, "Erro!" End If End If Else MsgBox "Ocorreu um erro ao incluir o produto.", _ vbExclamation, "Erro!" Cancel = True End If Else MsgBox "Ocorreu um erro ao incluir a venda.", _ vbExclamation, "Erro!" Cancel = True End If End If Exit_txtProdutoQtd_AfterUpdate:

89

Exit Sub Err_txtProdutoQtd_AfterUpdate: Select Case status Case 1, 3 MsgBox "Cdigo do produto invlido!", vbExclamation, "Erro!" Case 2 MsgBox "Quantidade do produto invlida!", vbExclamation, "Erro!" Case 5 MsgBox "Data invlida!", vbExclamation, "Erro!" Case 6 MsgBox "Cdigo do cliente invlido!", vbExclamation, "Erro!" Case 7 MsgBox "Ocorreu um erro ao incluir a venda!", vbExclamation, "Erro!" Case 8 MsgBox "Ocorreu um erro ao incluir o produto!", vbExclamation, "Erro!" Case 9 MsgBox "Ocorreu um erro ao atualizar o estoque!", vbExclamation, "Erro!" Case Else MsgBox "Ocorreu um erro. O sistema informou a seguinte mensagem:" & _ vbCrLf & vbLf & Err.Description, vbExclamation, "Erro!" End Select Cancel = True Resume Exit_txtProdutoQtd_AfterUpdate End Sub

Obs: Apenas para esclarecimento sobre o tratamento de erros lembre-se que este no um sistema real. Sendo assim os cdigos foram colocados de qualquer maneira para atingir o objetivo do curso: aprender a utilizar os objetos. Da maneira que foram implementados os registros um erro no meio do processo impediria que o final do registro de uma venda ou de um produto ocorresse, deixando os dados corrompidos. Em um sistema real deveriam ser utilizadas transaes para garantir que todos os processos ocorressem integralmente, do incio ao fim, ou ento que todas as etapas fossem canceladas e os dados retornassem ao estado original. No poderamos, por exemplo, ter uma venda registrada sem que o estoque fosse atualizado. Mas isto outra histria... 11. Atualizao das informaes: Aps o registro de um produto as informaes exibidas devem ser atualizadas e o foco retornar para o campo de digitao de produto e quantidade, a fim de que ele esteja pronto para efetuar novo registro. Para executar esta tarefa criaremos um procedimento no evento Aps Atualizar do campo txtProdutoQtd.
Private Sub txtProdutoQtd_AfterUpdate() btnNovo.SetFocus txtProdutoQtd.SetFocus txtProdutoQtd = Null Call preencheVenda Call atualizaLista End Sub

90

12. Excluso de produto da venda: Caso tenhamos que excluir um nico produto da venda, antes que ela seja fechada, deveremos desfazer os passos executados no registro do mesmo. Para isto devemos criar um procedimento no evento Ao Clicar do boto btnExcluirProduto. Ele ser encarregado de solicitar ao usurio o cdigo do produto que dever ser excludo, checando sua validade, atualizando o estoque e finalmente excluindo o produto, sendo que os dados de exibio devem ser novamente atualizados atravs de uma chamada ao procedimento genrico atualizaLista(). Repare que no excluiremos o produto em si, mas apenas o seu registro na venda.
Private Sub btnExcluirProduto_Click() Dim objDetalheVenda As New clsDetalheVenda Dim objProduto As New clsProduto Dim codigoProduto As String If Not IsNull(txtCodigoVenda) Then codigoProduto = InputBox("Informe o cdigo do produto a ser excludo:", _ "Excluso de Produto") Else Exit Sub End If If codigoProduto <> "" Then If IsNumeric(codigoProduto) Then If objDetalheVenda.obter(CLng(codigoProduto), CLng(txtCodigoVenda)) Then If objProduto.obter(CLng(codigoProduto)) Then If objProduto.subirEstoque(objDetalheVenda.qtdProduto) Then If objDetalheVenda.excluir Then MsgBox "O produto foi excludo com sucesso!", _ vbInformation, "Excluso de Produto" Call atualizaLista Else MsgBox "Ocorreu um erro durante a excluso do produto!", _ vbExclamation, "Excluso de Produto" End If End If End If End If Else MsgBox "Cdigo de produto invlido!", _ vbExclamation, "Excluso de Produto" End If End If End Sub

Obs: Veja que no inclu tratamento de erro neste procedimento. Tambm no inclu e nem incluirei em vrios outros. Esta tarefa dever ser executada no evento Ao Praticar do objeto LeitorAplicado. 13. Cancelamento da venda: Assim como podemos excluir um produto registrado tambm podemos cancelar uma venda inteira. Para executar esta tarefa deveremos desfazer todos os passos executados para registrar todos os produtos at o momento. O procedimento responsvel ser includo no evento Ao Clicar do boto btnCancelar. O
91

cancelamento dever contemplar a busca do conjunto de registros dos produtos da venda, atravs do mtodo getListaProduto() do objeto detalhe de venda, que sero verificados um a um para retorno ao estoque. Em seguida a venda ser excluda, ocasionando a excluso dos registros associados na tabela DetalheVenda pelo mecanismo do relacionamento em cascata. Aps a excluso os campos sero limpos para iniciar nova venda.
Private Sub btnCancelar_Click() Dim Dim Dim Dim objVenda As New clsVenda objDetalheVenda As New clsDetalheVenda objProduto As New clsProduto rstLista As Recordset

If objVenda.obter(Nz(txtCodigoVenda, -1)) Then If MsgBox("Confirma o cancelamento da venda?", _ vbQuestion + vbYesNo, "Cancelar Venda") = vbYes Then Set rstLista = objDetalheVenda.getListaProduto(txtCodigoVenda) While Not rstLista.EOF Call objProduto.obter(rstLista("codProduto")) Call objProduto.subirEstoque(rstLista("qtdProduto")) rstLista.MoveNext Wend If objVenda.excluir Then MsgBox "A venda foi cancelada com sucesso.", _ vbInformation, "Cancelar Venda" Call limpaCampos Else MsgBox "Ocorreu um erro durante o cancelamento.", _ vbExclamation, "Cancelar Venda" End If End If End If End Sub

14. Fechamento do PDV: Para encerrarmos as atividades no PDV simplesmente iremos incluir um procedimento que fecha o formulrio no evento Ao Clicar do boto btnFechar.
Private Sub btnFechar_Click() DoCmd.close End Sub

Sistema de Exemplo Novamente ser disponibilizado o link para download do sistema de vendas, j atualizado com todos os recursos do estado de desenvolvimento em que se encontrar o projeto.

92

Continua a recomendao de consultar o exemplo apenas para tirar dvidas e verificar como foi feito, alm de ver o sistema em funcionamento. Treinem bastante, digitem, testem, faam o crebro pedir gua. Esta a nica maneira de realmente assimilar o conhecimento. Segue o link para download:
Venda00.zip

Caso ainda no esteja com uma tela principal abra o formulrio FVenda diretamente na janela Banco de Dados (Access 2003-) ou no Painel de Navegao (Access 2007+). Concluso Depois desta exaustiva etapa foi possvel percebermos o quanto os objetos devem interagir para chegar ao fim de uma tarefa. Funes, procedimentos, eventos, mtodos e atributos trabalham em conjunto para produzir resultados que atendam as necessidades do usurio. Este artigo foi dedicado a demonstrar de maneira maante a criao, utilizao e troca de informaes entre objetos em meio aos cdigos do restante do sistema, por isso mesmo foi bem mais longo que os demais. Alm de toda a programao em si foi possvel trabalharmos apresentando um sistema interessante, til e muito comum no dia a dia, do qual muitos tinham a curiosidade de conhecer e compreender a forma de implementao: o PDV. Mas nosso sistema de vendas ultramoderno, com tecnologia orientada a objetos, no estaria completo sem ele, no mesmo? Aqui praticamente encerramos a parte mais trabalhosa. De agora em diante veremos apenas a finalizao do sistema, algo mais esttico do que funcional, alm de um pouco mais teoria nos artigos seguintes, com a apresentao do Genesis, a ferramenta case para VBA que facilitar a sua vida, e a abordagem sobre utilizao de objetos no MSAccess para aprimorar seus projetos.

93

Classes VIII - Finalizao do Sistema


Eis que chegamos ltima etapa da criao do nosso sistema de vendas orientado a objetos. Finalmente teremos o sistema totalmente pronto e funcional para que seja possvel visualizar a aplicao de todos os conceitos aprendidos nesta jornada de busca do conhecimento. A finalizao do aplicativo no trar exatamente alguma novidade, mas apenas o fechamento do projeto, ao criarmos a interface grfica de gerenciamento principal do software e os relatrios para exibio dos dados. A exceo ser para a apresentao do cupom de venda, que ser um exemplo bastante simplificado de um cupom fiscal de verdade, utilizado em sistemas comerciais.

A ltima Funo Auxiliar Para montar o cupom de venda, no formato de um cupom fiscal, necessitaremos de uma funo que busque a lista de produtos e acrescente os dados da empresa ou do sistema, alm dos dados do cliente, caso haja algum especfico. Assim sendo deveremos incluir mais uma funo no mdulo Funcoes criado anteriormente, que ser chamada de cupomVenda(), a qual utilizar a funo listaProdutos() criada anteriormente. A funo cupomVenda() receber como parmetro o cdigo da venda para a qual o cupom deve ser retornado. Este ser o cdigo da funo:
Function cupomVenda(argCodVenda As Long) As String Dim objVenda As New clsVenda If Not objVenda.obter(argCodVenda) Then cupomVenda = "" Exit Function End If cupomVenda cupomVenda cupomVenda cupomVenda cupomVenda cupomVenda vbCrLf cupomVenda = = = = = = "VENDA OO" cupomVenda cupomVenda cupomVenda cupomVenda cupomVenda & & & & & & vbCrLf "Sistema de Venda Orientado a Objetos" & vbCrLf "Av. do Passa 4 nr 171, Centro" & vbCrLf "Objetolndia - OB" & vbCrLf "Fone: (0xx62) 3322-4455" & vbCrLf "++++++++++++++++++++++++++++++++++++++++" &

= cupomVenda & Format(objVenda.dataVenda, "dd/mm/yy") & _ " Venda: " & _ Format(objVenda.codVenda, "000000") & vbCrLf cupomVenda = cupomVenda & vbCrLf cupomVenda = cupomVenda & " CUPOM FISCAL" & vbCrLf cupomVenda = cupomVenda & vbCrLf cupomVenda = cupomVenda & listaProdutos(argCodVenda)

cupomVenda = cupomVenda & "++++++++++++++++++++++++++++++++++++++++" & vbCrLf

94

cupomVenda = cupomVenda & vbCrLf cupomVenda = cupomVenda & "TOTAL _ FormatCurrency(objVenda.getValorTotal) & vbCrLf cupomVenda = cupomVenda & vbCrLf If Not IsNull(objVenda.codCliente) Then cupomVenda = cupomVenda & "Cliente: " & Format(objVenda.objCliente.codCliente, _ "0000") & vbCrLf cupomVenda = cupomVenda & "CPF: " & objVenda.objCliente.cpf & vbCrLf cupomVenda = cupomVenda & "Nome: " & objVenda.objCliente.nomeCliente & vbCrLf Else cupomVenda = cupomVenda & "Venda ao consumidor" & vbCrLf End If cupomVenda = cupomVenda & vbCrLf cupomVenda = cupomVenda & "Obrigado e volte Sempre..." End Function

" &

Veja que no caso de no haver um cliente registrado para a venda ela ser exibida como Venda ao consumidor, j que nem sempre em uma venda necessrio identificar o cliente. Pronto. Agora, a partir de qualquer local do nosso projeto, poderemos buscar um cupom de venda apenas utilizando a funo e passando o cdigo da venda como parmetro. O Formulrio Principal O formulrio principal o que se abre automaticamente assim que o aplicativo iniciado e ser o responsvel por permitir acesso aos demais itens do sistema. A seguir sero definidas as caractersticas para o formulrio principal. Assim como nos anteriores as especificaes de design tambm so apenas sugestes, porm as informaes de origem de dados, bloqueio de campos e formatos de dados so obrigatrias, sob pena de, novamente, no obter a funcionalidade desejada. Como de praxe as especificaes estaro marcadas com um asterisco vermelho ao lado. No formulrio teremos apenas os rtulos como o nome e a descrio do sistema, alm de mais seis botes de comando para acesso aos formulrios de cadastro, PDV e relatrios, com seus respectivos rtulos. Formulrio
Nome: FPrincipal Altura: Cabealho (4cm) / Detalhe (11cm) Cor do fundo: Cabealho (Azul) / Detalhe (Cinza claro) Legenda: Sistema de Vendas Seletores de registro: No Botes de navegao: No Linhas divisrias: No Barras de rolagem: Nenhuma Caixa de controle: Sim Boto Fechar: Sim Botes Min Max: Nenhum

95

Queremos que nosso formulrio seja exibido automaticamente assim que o Sistema de Vendas for aberto. Tambm no queremos que o painel de navegao (ou a Janela Banco de Dados) seja exibido. Para isso vamos realizar o seguinte procedimento: No Access 2003 ou inferior: V ao menu Ferramentas -> Inicializar. Em seguida, na caixa de texto Exibir Formulrio/Pgina informe o nome do formulrio FPrincipal. Desmarque a opo Exibir Janela Banco de Dados e clique em Ok. No Access 2007: V ao Boto do Office, no canto superior esquerdo da tela, depois em Opes do Access -> Banco de Dados Atual. Em seguida, na caixa de texto Exibir Formulrio informe o nome do formulrio FPrincipal. Desmarque tambm a opo Exibir Painel de Navegao e clique em Ok. No Access 2010: V ao menu Arquivo, depois em Opes -> Banco de Dados Atual. Em seguida, na caixa de texto Exibir Formulrio informe o nome do formulrio FPrincipal. Botes de Comando
Nome: btnCliente * Nome: btnProduto * Nome: btnPDV * Nome: btnListaCliente * Nome: btnListaProduto * Nome: btnRelVenda *

Como sugesto de design os campos devero estar posicionados conforme demonstrado na figura a seguir, assim como devem ser adicionadas as legendas dos botes, as quais no foram descritas anteriormente:

96

Cdigos do Formulrio Para que nosso formulrio seja capaz de permitir o acesso aos itens do sistema deveremos programar as funcionalidades necessrias que permitam abrir os formulrios e relatrios existentes. Por ser um processo bastante simples e repetitivo no detalharei cada um dos comandos. Colocarei os comandos agrupados pelo seu tipo de funcionalidade. Todos os cdigos a seguir devero ser colocados no mdulo do formulrio FPrincipal. 1. Abertura dos formulrios de cadastro: Para abrirmos os formulrios de cadastro e o PDV utilizaremos o evento Ao Clicar dos botes com o comando OpenForm.
Private Sub btnCliente_Click() DoCmd.OpenForm "FCliente" End Sub Private Sub btnPDV_Click() DoCmd.OpenForm "FVenda" End Sub Private Sub btnProduto_Click() DoCmd.OpenForm "FProduto" End Sub

2. Abertura dos relatrios: Para abrirmos os relatrios utilizaremos o evento Ao Clicar dos botes com o comando OpenReport.

97

Private Sub btnListaCliente_Click() DoCmd.OpenReport "RCliente", acViewPreview End Sub Private Sub btnListaProduto_Click() DoCmd.OpenReport "RProduto", acViewPreview End Sub Private Sub btnRelVenda_Click() DoCmd.OpenReport "RVenda", acViewPreview End Sub

3. Maximizao do formulrio: Para que o formulrio principal se mantenha maximizado o tempo todo utilizaremos o comando Maximize nos eventos Ao Abrir e Ao Ativar do mesmo.
Private Sub Form_Activate() DoCmd.Maximize End Sub Private Sub Form_Open(Cancel As Integer) DoCmd.Maximize End Sub

Relatrios Os dados cadastrados e as vendas registradas podero ser exibidos e preparados para impresso atravs dos relatrios que criaremos. Como no o objetivo deste curso no detalharemos a criao dos relatrios, sendo que apenas descreveremos a funo de cada um deles. Os relatrios Lista de Clientes e Lista de Produtos devem ser gerados de maneira simples a partir das tabelas Cliente e Produto, respectivamente. O Relatrio de Vendas deve ser gerado a partir da consulta CVenda. J o cupom fiscal ser um pouco mais detalhado, j que utiliza um controle no acoplado com a funo cupomVenda(). Lista de Clientes Responsvel por exibir os dados dos clientes cadastrados em ordem alfabtica de nomes. O nome dele ser RCliente.

Lista de Produtos Responsvel por exibir os dados dos produtos cadastrados em ordem alfabtica de descrio. O nome dele ser RProduto.
98

Relatrio de Vendas Responsvel por exibir os dados das vendas registradas, o nome dos clientes e os produtos componentes de cada venda. O nome dele ser RVenda.

Cupom de Venda O cupom de venda exibir os dados da venda em formato de um cupom fiscal simplificado. O nome dele ser RCupom. Ao invs do formulrio principal ele ser acionado a partir do PDV, ao clicarmos duas vezes no controle lblProdutos, o mesmo que exibe os produtos registrados no momento da venda no PDV. Para isto incluiremos o cdigo abaixo no evento Ao Clicar Duas Vezes no controle lblProdutos do formulrio FVenda.
Private Sub lblProdutos_DblClick(Cancel As Integer) If Not IsNull(txtCodigoVenda) Then DoCmd.OpenReport "RCupom", acViewPreview, , , , txtCodigoVenda End If End Sub

99

Repare que o cdigo da venda, que ser o parmetro para a funo cupomVenda(), ser passado atravs do argumento OpenArgs de abertura do relatrio. Ao abrir o formulrio o cdigo estar disponvel para a funo atravs da propriedade AbrirArgs do relatrio. Para montarmos o relatrio com o cupom criaremos apenas um controle caixa de texto no acoplado com o seguinte cdigo: =cupomVenda(Nz([AbrirArgs])) A funo buscar todas as informaes sobre a venda e seus produtos e retornar para o controle, que as exibir no relatrio. O controle ter o nome de txtCupom, a propriedade Pode Ampliar definida como Sim e a largura de 14 cm. O relatrio dever ter sua propriedade Popup definida como Sim. Veja abaixo como ser o modelo do cupom de venda no modo de visualizao:

Sistema de Exemplo Novamente ser disponibilizado o link para download do sistema de vendas, desta vez finalizado e totalmente funcional.

100

Continua a recomendao de consultar o exemplo apenas para tirar dvidas e verificar como foi feito, alm de ver o sistema em funcionamento. O treinamento ainda a maneira recomendada de aprender. Segue o link para download
Venda00.zip

A fim de auxiliar na resoluo de problemas enfrentados pelos leitores que tentam colocar em prtica os ensinamentos deixo aqui a informao sobre quais referncias esto marcadas no editor do VBA. Para isto veja a imagem a seguir.

A verso utilizada para montar o exemplo foi o Access 2007, sendo que salvei o mesmo no formato do Access 2000, para que todos pudessem acess-lo normalmente. Concluso Aps alguns meses de suado trabalho, de minha parte, e bastante estudo e treinamento, da parte de vocs leitores, conseguimos finalizar nosso projeto, criando o sistema de vendas orientado a objetos. Alm da utilizao massiva de objetos para desempenhar as funes de cadastro, clculo, validao e acesso a dados, aprendemos como criar um PDV simples e tambm um modelo de cupom de venda, tambm bastante simples.
101

Depois de toda esta cansativa rotina de programao, no caso da digitao de todos aqueles cdigos das classes, no seria maravilhoso que esta tarefa pudesse ser simplificada? No seria timo que no tivssemos que nos preocupar com a montagem dos cdigos SQL dos mtodos das classes, nem com os mtodos de acesso s propriedades? E quando alteramos um atributo da classe, ser que temos que refazer tudo de novo? Pois bem, este ser o assunto do prximo artigo, no qual ser apresentada a ferramenta Genesis, um aplicativo que cria as classes para o nosso projeto. Voc apenas informa quais so os campos das tabelas e sua descrio, ento o Genesis cria o banco de dados e as classes em um piscar de olhos. Alterou alguma coisa? Basta gerar novamente a classe e importar para seu projeto outra vez. Simples assim. Alm da criao das classes com o Genesis, veremos dicas de como agilizar a importao e a remoo de mdulos de nosso projeto, a fim de evitar que tenhamos que importar ou remover um por um, o que j nos trs um grande aumento de produtividade.

102

Classes IX - Genesis - A Ferramenta Case


Frequentemente ns, profissionais e amadores da rea de informtica, nos deparamos com algumas tarefas extremamente chatas existentes no desenvolvimento de sistemas, algumas delas relacionadas aos trabalhos repetitivos e cansativos, como, por exemplo, a documentao de atributos e a programao de mtodos bsicos de classes, ou seja, aqueles que atribuem ou retornam valores, buscam, salvam ou excluem um objeto, entre outros. Segundo criadores de sistemas anlogos, o trabalho de programao pode ser reduzido, em mdia, at 60%, ao se utilizar os geradores de cdigo. Isto permite que o profissional concentre-se apenas em implementar os mtodos inerentes s regras do negcio, diminuindo consideravelmente o tempo necessrio para a concluso do projeto. Algumas das ferramentas de gerao de cdigo existentes mantm o seu foco na criao de classes a partir de um Banco de Dados pronto, conectando-se ao mesmo e buscando as informaes sobre a estrutura das tabelas e relacionamentos. Outras geram as classes a partir dos modelos de projeto. Existem tambm algumas especializadas em modelar o prprio esquema do Banco de Dados, fornecendo os scripts SQL para a criao do mesmo. Umas mais simples, outras nem tanto, a verdade que nunca encontramos aquela que atende s nossas necessidades completamente. O Genesis, que uma opo a mais, uma ferramenta de desenvolvimento que busca a unio entre os esforos de documentao, criao do Banco de Dados e programao, auxiliando na construo do software, a partir dos dados inseridos pelo usurio uma nica vez, nestes trs aspectos bsicos: Gerao do Banco de Dados, com todas as tabelas e seus respectivos relacionamentos; Gerao do cdigo das classes, uma para cada tabela, contendo os atributos e os mtodos bsicos, alm da classe de conexo com o BD e o mdulo de importao de classes; Gerao dos relatrios de estrutura das tabelas, dicionrio de dados e scripts de criao das tabelas, podendo servir para anlise do esquema ou fazer parte da documentao do sistema.

103

Para alcanarmos nosso objetivo de programar orientado a objetos, agora utilizando ferramentas que aumentem nossa produtividade, nesta etapa vamos conhecer um pouco mais do Genesis, e descobrir como ele poder nos ajudar em nossas atividades. Utilizando o Genesis A ferramenta foi desenvolvida no padro do Access 2000, para que possa ser utilizado pelo maior nmero de usurios. Alm das bibliotecas padro do Access, ele utiliza as seguintes referncias, na ordem em que aparecem:
OLE Automation Microsoft ActiveX Data Objects 2.1 Library Microsoft DAO 3.6 Object Library

Caso sua verso possua referncias diferentes, desmarque as existentes, que devero estar indicadas como " AUSENTES ", e procure uma equivalente, a mais prxima possvel. Normalmente elas tm o mesmo nome, alterando apenas o nmero da verso. Quando encontr-la marque, compile e teste o programa at que encontre todas as bibliotecas necessrias. Visando padronizar o estilo das classes e do banco de dados que sero criados, siga os padres propostos para nomear tabelas e atributos, alm de inserir descries conforme ser apresentado nos prximos tpicos. Caso voc utilize outro padro a funcionalidade no ser afetada, mas os atributos, as tabelas e as classes geradas pelo Genesis tem seus

104

nomes modificados ou so acrescentadas iniciais que podem prejudicar a esttica do sistema caso as regras no seja seguidas. Como todo bom software livre o cdigo do Genesis aberto, e voc pode realizar modificaes a seu critrio para personalizar a padronizao dos nomes de tabelas, atributos e classes. Campos e Comandos do Sistema A figura abaixo identifica cada um dos botes de comando e campos da tela principal do Genesis. Utilize-a como referncia para os tpicos seguintes, que esclarecero com mais detalhes o funcionamento do sistema.

Trabalhando com Tabelas Padro proposto para nomes de tabelas: :: Primeira letra em maiscula; :: Letras restantes em minscula; :: Nomes compostos por mais de uma palavra separados por maiscula; :: No utilize acentos ou cedilha. Ex: Departamento, Produto, Cliente, TipoCliente e CategoriaProduto
105

Quando o Genesis cria uma classe para a tabela acrescentado o prefixo cls no incio do nome da mesma, resultando em uma classe com a inicial minscula e as separaes em maiscula. Os exemplos acima ficariam assim: Ex: clsDepartamento, clsProduto, clsCliente, clsTipoCliente e clsCategoriaProduto Caso voc colocasse um nome, por exemplo, em minscula, no haveria a separao das palavras. Nomes com underscore tambm prejudicariam a esttica, resultando em nomes de classe da seguinte maneira: Ex: clsdepartamento, clsPRODUTO, clsTipo_Cliente e clsCATEGORIA_PRODUTO Para criar uma nova tabela clique no boto Novo (1). Ser criado um registro em branco. Coloque o nome da tabela que deseja criar no campo Tabela/Classe (2), e a descrio da mesma ao lado, no campo Descrio (3), para que esta seja adicionada aos relatrios de documentao (estrutura da tabela e dicionrio de dados) e ao relatrio SQL. Apenas as tabelas que estiverem com o campo Criar (4) marcado sero exportadas quando o boto Exportar Classes (5) for clicado. Para excluir uma tabela clique no boto Excluir (7). Se uma tabela for excluda todos os seus campos tambm sero automaticamente eliminados. Para navegar entre tabelas e pesquisar utilize os botes de navegao (5) e pesquisa (6). Depois de criada a tabela inclua os atributos. Trabalhando com Campos da Tabela Padro proposto para os campos das tabelas: :: Primeira letra em minscula; :: Letras restantes em minscula; :: Nomes compostos por mais de uma palavra separados por maiscula; :: Campos de chave primria ou estrangeira em que ser utilizado um cdigo inclua a inicial cod ou id no nome do campo; :: No utilize acentos ou cedilha. Ex: codDepartamento, descricao, valor, codCliente, idCliente, dataNascimento, mesEntradaProduto e categoriaProduto Quando o Genesis cria um atributo de classe para um campo de tabela acrescentado um prefixo que indica o tipo de dado, conforme a descrio a seguir:

106

String (texto) str Date/Time (data/hora) dtm Integer (inteiro) int Long (inteiro longo) lng Single (decimal simples) sgl Double (decimal duplo) dbl Currency (valor monetrio) cur Object (objeto) obj Recordset (conjunto de registros) rst Boolean (lgico) boo

Alm disso a primeira letra do nome do atributo convertida para maiscula para manter o padro. Os exemplos acima ficariam assim: Ex: intCodDepartamento, strDescricao, curValor, intCodCliente, dtmDataNascimento, intMesEntradaProduto e strCategoriaProduto lngIdCliente,

Os prximos tpicos descrevem como configurar os campos da tabela a serem criados. Chave Primria Marque o campo PK (17) para indicar que o atributo ou faz parte da chave primria da tabela. Ao indicar um campo como chave primria ele aparecer na caixa de combinao do campo Atributo FK (22) de outras tabelas, para que possa ser usado como chave estrangeira. Obs: Atributos do tipo Memo ou Object no podem ser chave primria. Nome do Atributo Informe no campo Atributo (18) neste campo o nome do atributo, de acordo com o padro apresentado para nomes de campos de tabela. Campo Obrigatrio Marque este campo para indicar que o atributo obrigatrio e no pode conter um valor nulo. Tipo de Dado Escolha o tipo de dado para o atributo. Os tipos possveis so:
String Texto com at 255 caracteres; Memo Texto com tamanho indeterminado; Date/Time Data e/ou hora, tamanho 8 bytes; Integer Nmero inteiro com 2 bytes de tamanho (-32.768 a 32.767); Long Nmero inteiro longo com 4 bytes de tamanho (-2.147.483.648 a 2.147.483.647); Single Decimal com ponto flutuante com 4 bytes de tamanho (3,402823E38 a 1,401298E-45); Double Decimal com ponto flutuante com 8 bytes

107

de tamanho (1,79769313486232E308 a 4,94065645841247E-324); Currency Nmero com ponto fixo com 15 dgitos esquerda da vrgula e 4 dgitos direita da vrgula, utilizado para clculos de preciso por envolver valores em dinheiro (-922.337.203.685.477,5808 at 922.337.203.685.477,5807); Object Endereos com tamanho de 4 bytes, que se referem a objetos (Imagens, documentos, planilhas, etc...); Boolean Nmero com tamanho de 2 bytes que representa os valores lgicos True e False. A equivalncia entre os valores e os nmeros 0 para False e qualquer outro nmero para True.Um valor True convertido para nmero retorna 1.

Tamanho do Campo Informe no campo Tamanho (21) a quantidade mxima de caracteres para campos do tipo String. O tamanho mnimo permitido 1 e o mximo 255 caracteres. Caso seja colocado um valor fora do intervalo o Genesis informar o erro e colocar o valor padro. Os outros tipos de campo no podem ter tamanho de campo diferente do padro. Estes so os tamanhos padro para cada tipo de campo:
String 30; Memo 0 (No h limite definido!); Currency, Double e Date/Time 8; Integer e Boolean 2; Object, Long e Single 4.

Chave Estrangeira (Foreign Key) Indique no campo Atributo FK (22) o atributo (chave primria da tabela de destino) que servir de referncia para a chave estrangeira na tabela qual estar vinculado. Os possveis atributos so listados e podem ser acessados inclusive a partir da mesma tabela. Isto ocorre quando, por exemplo, um atributo referencia outro atributo da mesma tabela, criando um auto-relacionamento. Esta situao poderia ocorrer em casos como o de uma empresa onde um funcionrio chefe de outro, ou um departamento subordinado a outro, ou quando um produto componente de outro, e a melhor forma de mapear este relacionamento seja uma tabela nica. Voc s pode criar uma chave estrangeira que referencie um campo de chave primria de outra tabela, ou da mesma tabela, se os dois campos forem do mesmo tipo. Como exemplo um campo do tipo Integer no pode referenciar um String, e assim sucessivamente. Ao vincular campos de chave estrangeira o Genesis criar os relacionamentos com integridade referencial, porm no incluir os eventos em cascata para atualizao e excluso de dados. Caso seja necessria, esta configurao dever ser feita manualmente pelo desenvolvedor no banco de dados criado pelo Genesis.

108

Tipo de Chave Estrangeira Ao incluir uma chave estrangeira na tabela com certeza voc far uso desta ligao tambm nas classes. Logo devemos informar o tipo de chave estrangeira no campo Tipo FK (23). Para compreender a utilidade deste recurso, imagine os dois casos a seguir: No primeiro temos uma relao entre Fita e Categoria, duas classes de uma locadora de fitas de vdeo. Nesta locadora o valor de locao no colocado individualmente nas fitas. Elas so classificadas em categorias, como Lanamento, Catlogo, Infantil e Promoo, para que seja facilitado o trabalho de alterao de preos. Ao se modificar o valor de locao de uma categoria todas as fitas estaro atualizadas automaticamente. 1 Caso: Descrio de Tipo (DT)

Veja bem, no cdigo da classe Fita dever existir um mtodo novaLocacao() ou algo parecido, que ser responsvel pela efetivao de uma locao de fita. Para calcular o valor da locao o mtodo teria que instanciar a classe Categoria, verificar a que categoria a fita pertence, atravs do atributo intCategoria, e solicitar a ela o valor da locao. At a tudo bem, mas ento qual a utilidade do recurso Descrio de Tipo? Pois bem, este recurso cria automaticamente um atributo do tipo de objeto da classe que precisa ser instanciada (Categoria), e toda vez que o atributo de chave estrangeira intCategoria atualizado, a referncia classe Categoria tambm atualizada atravs do atributo objeto criado. No exemplo dado os atributos da classe Categoria podem ser acessados atravs da classe Fita, da seguinte maneira:
'Declaramos algumas variveis conforme a necessidade Dim curPrecoLocacao As Currency Dim strDescricao As String 'Declaramos e instanciamos o objeto Fita Dim objFita As New clsFita 'Obtemos o objeto fita cujo cdigo 1012

109

objFita.obter(1012) 'Acessamos os atributos da classe Categoria strDescricao = objFita.objCategoria.descricao curPrecoLocacao = objFita.objCategoria.valorLocacao 'De dentro da classe Fita o acesso seria mais simples, da seguinte maneira strDescricao = objCategoria.descricao curPrecoLocacao = objCategoria.valorLocacao

Vamos analisar agora o segundo caso, o Vetor de Registro (VR). A descrio de tipo serve para buscarmos um valor nico em outra classe, mas imagine, como no exemplo abaixo, um Departamento que queira obter uma relao com todos os Funcionrios que trabalham no mesmo. Uma soluo seria criar um Recordset, executar uma consulta e navegar atravs dele para coletarmos os dados desejados. Visando facilitar este trabalho, ao se definir uma chave estrangeira como VR, o Genesis inclui automaticamente um mtodo vetorNomeDaClasse() na classe atual, cujo valor de retorno exatamente um Recordset contendo todos os objetos que pertencem ao objeto que possui o mtodo, neste caso todos os funcionrios que pertencem ao departamento. 2 Caso: Vetor de Registro (VR)

Veja no modelo de classes que, aps definir intDepartamento como chave estrangeira do tipo VR, foi criado o mtodo vetorFuncionario() na classe Funcionario, o qual utiliza o valor do atributo para saber a que Departamento pertence o funcionrio. A consulta feita via SQL, diretamente no banco de dados, mas a atribuio dos valores foi originada anteriormente pelos atributos do objeto. Alm disso foram includos dois atributos na classe estrangeira, aquela que necessitava da relao de registros da outra classe. Foram criados um objeto da classe que contm o VR e um Recordset que recebe os registros retornados da consulta. A operao executada toda vez que os atributos chave da classe estrangeira so atualizados. Neste

110

caso quando codDep alterado pelo procedimento Property Let codDep(), os atributos objFuncionario e rstFuncionario tambm so atualizados pelo procedimento. A partir da podemos navegar pelo Recordset rstFuncionario normalmente atravs da classe Departamento, ou acessar suas propriedades, da seguinte maneira:
'Navegando pelo Recordset, buscando o prximo registro objDepartamento.rstFuncionario.moveNext 'Verificando a quantidade de registros de funcionrios objDepartamento.rstFuncionario.recordCount 'Acessando um atributo do funcionrio objDepartamento.rstFuncionario.Fields("nome") Obs: Perceba que ao acessarmos um atributo pelo recordset devemos utilizar o nome original do mesmo, pois o prefixo str adicionado existe somente dentro da classe.

Devemos nos lembrar que em nenhum momento o programador necessita instanciar as classes ou os Recordsets para implementar estes recursos, pois o Genesis deixa todo o cdigo pronto. Ao programador cabe apenas instanciar a classe com a qual vai trabalhar e fazer uso das facilidades apresentadas. Importante: * O recurso Descrio de Tipo s implementado automaticamente quando a classe estrangeira possui chave primria nica. Quando a classe estrangeira possui chave primria composta por mais de um atributo o Tipo FK ser ignorado. * O recurso Vetor de Registro s implementado automaticamente quando as duas classes possuem chave primria nica. Nas classes em que a chave primria composta de mais de um atributo o Tipo FK ser ignorado. Ordem da Chave Estrangeira Para definir a ordem das chaves estrangeiras no banco de dados a ser criado devemos inform-la no campo Ordem (24). Este recurso tem a nica, mas no menos importante, utilidade de separar chaves estrangeiras para uma mesma chave primria da tabela estrangeira. Isto ocorre quando temos dois ou mais atributos semelhantes em uma tabela fazendo referncia ao mesmo atributo FK de outra tabela. Raramente isto acontece, mas no caso de ocorrer lembre-se de utilizar a Ordem, ou ocorrer um erro tanto na criao da tabela quanto da classe. Para ilustrar melhor a necessidade veja um exemplo prtico. Em um hospital os mdicos trabalham por escala de planto. Digamos que o sistema do hospital mantm o registro das equipes que concorrem ao planto diariamente. Suponha que o BD esteja organizado mais ou menos assim:

111

Podemos notar claramente que o sistema guarda os registros do mdico que estava escalado para o planto e o mdico que efetivamente tirou o planto. Quando os dois cdigos so iguais significa que no houve substituio. Mas o importante perceber que tanto o atributo MedicoEscalado quanto MedicoSubstituto da tabela Plantao referenciam a mesma chave primria da tabela Medico, ou seja, o atributo Codigo. Para que o Genesis no confunda um caso como este com um caso de chave composta por mais de um atributo, voc deve diferenciar os atributos FK definindo Ordens diferentes para os mesmos. Poderamos definir o campo Ordem para o atributo MedicoEscalado com o valor 1 e para MedicoSubstituto com o valor 2, por exemplo. Nos casos de chave composta deve ser mantida a mesma Ordem para todos os atributos, para o caso de apenas um conjunto de chave estrangeira, ou uma ordem para cada conjunto, no caso de haver mais de um. Em resumo, a Ordem representa uma seqncia, ou conjunto, de chaves estrangeiras para uma mesma chave ou conjunto de chaves primrias. Descrio do Atributo Informe no campo Descrio (25) os detalhes que esclaream a funo do atributo, para que ela seja adicionada aos relatrios de documentao (estrutura da tabela e dicionrio de dados) e aos comentrios da classe. A descrio pode ter at 150 caracteres. Lembre-se de utilizar texto que deixe claro a utilidade do atributo, e que esteja livre de erros de ortografia ou gramtica, zelando, assim, pela esttica da documentao e do sistema que ser gerado. Para campos de chave primria, a sugesto colocar a sigla PK (Primary Key) no incio da descrio, para identific-los como tal. J para campos de chave estrangeira, a sugesto colocar a sigla FK (Foreign Key). Excluindo um Atributo Para excluir um atributo basta clicar no boto que contem uma lixeira (26) e confirmar quando o sistema solicitar. Gerando uma Classe

112

Para gerar o cdigo da classe atual clique no boto Gerar Classe (13). O cdigo ser criado e apresentado em um formulrio independente. O campo Criar (4) no precisa estar marcado para a utilizao deste comando. Um conhecimento prvio dos conceitos de Orientao a Objetos auxilia na compreenso dos cdigos desta parte. Em caso de dvida consulte o restante do material ou uma das referncias apresentadas anteriormente para se aprofundar no assunto. Primeiramente vamos conhecer a estrutura completa de uma classe gerada pela ferramenta Genesis.

Nome da Classe Conforme descrito anteriormente, o nome da classe criado adicionando-se o prefixo cls ao nome da tabela. clsNomeDaTabela Atributos criado um atributo para cada campo da tabela. Para cada campo de chave primria criado tambm um atributo de backup, utilizado pelo mtodo salvar(), pois no caso de ocorrer uma alterao de um atributo chave no seria mais possvel atualizar o BD pelo novo valor. Neste caso o mtodo salvar() no faria efeito, ento ele utiliza o valor do backup para executar a consulta atualizao, em seguida altera todos os atributos para os novos valores, caso a operao ocorra com sucesso. Alm destes atributos particulares de cada classe, so criados dois atributos genricos,
113

booChaveAlterada e booAtributoAlterado, ambos lgicos, utilizados para indicar alteraes nos valores de atributos chaves e atributos comuns, respectivamente. Estes atributos no possuem funo especfica, e podem ser utilizados pelo desenvolvedor conforme a necessidade, quando este quiser saber se uma chave ou atributo tiveram seus valores modificados. Os atributos do tipo objeto e recordset automticos so criados caso exista algum atributo do tipo Descrio de Tipo (DT) ou Vetor de Registro (VR).
booChaveAlterada booAtributoAlterado bkpAtributoIdentificador1 tipoAtributoIdentificador1 : : [bkpAtributoIdentificadorN] [tipoAtributoIdentificadorN] tipoAtributoExtra1 [tipoAtributoExtraN]

Mtodos de acesso aos Atributos Para cada atributo originado de um campo da tabela so criados dois mtodos, um para atribuir valor (Property Let, ou Property Set para objetos) e outro para recuperar valor (Property Get). Dentro destes mtodos podem existir funes extras, como as que implementam o VR, dependendo do contexto.
Property Get chaveAlterada ( ) Property Get atributoAlterado( ) Property Property : : Property Property Let atributoIdentificador1( ) Get atributoIdentificador1( ) Let atributoIdentificadorN( ) Get atributoIdentificadorN( )

Property Let atributo1( ) [Property Set atributo1( )] Property Get atributo1( ) : : Property Let atributoN( ) Property Get atributoN( ) [Property Get objNomeDoObjeto1( )] : : [Property Get objNomeDoObjetoN( )]

Mtodos construtor e destrutor Os mtodos construtor e destrutor so criados sem qualquer cdigo executvel. Apenas a estrutura deles gerada. Para utiliz-los basta inserir cdigo dentro deles. Todo cdigo que estiver dentro do mtodo class_initialize() ser executado sempre que um objeto da classe for instanciado. Da mesma forma todo cdigo dentro de um mtodo class_terminate() ser executado sempre que a classe encerrar a sua existncia, mas antes que isto realmente ocorra.
class_initialize( ) class_terminate( )

114

Mtodos de acesso ao Banco de Dados So os mtodos bsicos de acesso ao banco de dados, utilizados para realizar a persistncia dos objetos. O mtodo obter() utilizado para a recuperao de um objeto, recebendo como parmetro o(s) valor(es) do(s) atributo(s) chave da classe, e realizando uma consulta a partir deste(s) valor(es). O mtodo incluir() faz o oposto. Depois de definidos os valores dos atributos, este mtodo os coleta e inclui o novo objeto no banco de dados, tornando-o persistente. O mtodo salvar() grava as alteraes feitas aos atributos do objeto no banco de dados. Um objeto s pode ser salvo se j existia anteriormente. Objetos novos devem ser includos antes que se possa salv-los. Antes de salvar um objeto o mtodo verifica se o mesmo j no existe no banco de dados, utilizando o mtodo existe(), que apenas checa a existncia de um registro com o mesmo valor de chave na respectiva tabela. O mtodo excluir() apaga o registro referente ao objeto no banco de dados. Todos este mtodos retornam verdadeiro caso a operao ocorra com sucesso, ou falso em caso contrrio.
existe( ) incluir( ) excluir( ) obter( ) salvar( )

Limpar Mtodo que redefine todos os atributos de volta aos seus valores iniciais, ou seja, atribui o valor Nulo para todos os atributos e False para os atributos booChaveAlterada e booAtributoAlterado. O mtodo executado toda vez que um objeto excludo com sucesso, imediatamente aps o trmino da operao. Voc pode utiliz-lo a qualquer momento para "limpar um objeto".
limpar()

Vetor de Registro (VR) e Descrio de Tipo (DT) Os atributos e o mtodo criados para a implementao da descrio de tipo e do vetor de registro foram detalhados no item que trata sobre o campo TipoFK. So os objetos que referenciam outra classe para obter valores de atributos da mesma, no caso da DT, ou uma relao de registros, no caso do VR. Estes atributos e o mtodo so gerados apenas quando o usurio define uma chave estrangeira com um dos dois tipos.
[objNomeDaClasse] [rstNomeDaClasse] [objetoNomeDoObjeto] [vetorNomeDaClasse( )]

Gerando o Cdigo SQL da Tabela Para gerar o script SQL de criao da tabela clique no boto Gerar SQL (14). O cdigo ser criado e uma mensagem de confirmao informar o sucesso da operao. Visualizando o Cdigo SQL da tabela e o Cdigo da Classe Para visualizar o script SQL de criao da tabela clique no boto Cdigo SQL (16). O cdigo ser apresentado em um formulrio independente. O cdigo pode ser copiado ou alterado, mas esta alterao no far efeito na criao do Banco de Dados.
115

Para visualizar o cdigo da classe clique no boto Cdigo da Classe (15). O cdigo ser apresentado em um formulrio independente. O cdigo pode ser copiado ou alterado, mas sua alterao no far efeito na exportao das classes.

Visualizando o Relatrio de Estrutura das Tabelas Para visualizar o relatrio de estrutura das tabelas clique no boto Listar Tabelas (10). O relatrio com as informaes solicitadas ser apresentado. Visualizando o Dicionrio de Dados Para visualizar o dicionrio de dados clique no boto Dicionrio de Dados (11). O relatrio com as informaes solicitadas ser apresentado. Visualizando o Relatrio SQL Para visualizar o relatrio de cdigo SQL das tabelas clique no boto Cdigo SQL (12). O relatrio com as informaes solicitadas ser apresentado. Para export-lo para o Word utilize a funo Publicar com o MS Word Gerando o Banco de Dados Para gerar o banco de dados completo clique no boto Banco de Dados (8). A caixa de dilogo do Windows Salvar Como ser apresentada. Escolha a pasta e o nome do novo banco de dados e clique em Salvar. Caso no ocorram erros uma mensagem ser apresentada ao trmino do processamento informando que o BD foi gerado com sucesso. Para colocar o projeto em andamento, exporte as classes para uma pasta e, em seguida, importe-as para o BD que acaba de ser criado. Para isso abra o editor do Visual Basic e v ao menu Arquivo / Importar Arquivo e inclua as classes. Esta operao s permite a importao de uma classe por vez, ento seja paciente. Para ajudar com esta importao, ou mesmo a atualizao de classes, o Genesis inclui o mdulo IRModulos, que contem as funes importaClasses() e removeClasses(), que sero detalhadas em um tpico a parte.
116

Na ocorrncia de erros cada um deles ser informado, sendo que isto no interrompe o processo de criao do BD. As mensagens tentaro inform-lo sobre o erro para que seja corrigido. Ao final ser apresentada a mensagem de concluso, na qual constar o nmero de erros ocorridos. A mensagem final ir sugerir a voc que verifique e organize a estrutura esttica dos relacionamentos, pois eles so criados em ordem aleatria, fazendo com que as tabelas fiquem em posies que deixam as linhas dos relacionamentos cruzando-se entre si, de maneira totalmente catica. Nada que interfira no funcionamento, mas para que voc mantenha seu modelo adequadamente limpo, um pouco de ordem no far mal. A Classe de Conexo A classe aclConexaoBD ser includa no banco de dados criado, possibilitando a utilizao imediata a partir das outras classes ou qualquer outra funo que necessite acesso s tabelas via SQL. As classes geradas pelo Genesis j utilizam a classe de conexo. Importando e Removendo Mdulos A fim de evitar que os usurios tenham que importar ou remover mdulos individualmente do sistema, utilizando o menu do editor do VBA, ou a combinao de teclas Ctrl+M, foi includo no Genesis o mdulo IRModulos, que possui duas funes, uma para importar e outra para remover mdulos em grupo. Os procedimentos devem ser executados manualmente pelo desenvolvedor, que dever ajustar as variveis que indicam o caminho da pasta onde se encontram as classes, posicionar o cursor dentro do procedimento desejado e teclar F5 ou abrir o menu Executar => Executar Sub/UserForm no menu do editor do VBA. Este o cdigo dos procedimentos do mdulo:
Sub importaClasses() 'Como utilizar este procedimento para 'importar seus mdulos: primeiro crie as 'classes utilizando o Genesis, exporte-as 'para uma pasta qualquer, altere o endereo 'da varivel caminho abaixo, e execute o 'procedimento, posicionando o cursor dentro 'do cdigo e pressionando a tecla F5.

Dim fs, pasta, arquivos, arquivo Dim caminho As String Dim nomeCompleto As String

117

Dim nomeArquivo As String 'Altere o caminho da pasta para o 'local onde esto as suas classes 'a serem importadas. caminho = "C:\MeuProjeto\Classes" If caminho <> "" Then Set Set Set For fs = CreateObject("Scripting.FileSystemObject") pasta = fs.GetFolder(caminho) arquivos = pasta.Files Each arquivo In arquivos nomeArquivo = arquivo.Name If Left(nomeArquivo, 3) = "cls" Then nomeCompleto = arquivo.Path Application.VBE.ActiveVBProject.VBComponents.Import End If Next End If End Sub Sub removeClasses() 'Para remover os mdulos execute o 'procedimento posicionando o cursor 'dentro do cdigo e pressionando F5. Dim componentes, componente Dim nomeComponente As String Set componentes = Application.VBE.ActiveVBProject.VBComponents For Each componente In componentes nomeComponente = componente.Name If Left(nomeComponente, 3) = "cls" Then Application.VBE.ActiveVBProject.VBComponents.Remove componente End If Next End Sub

nomeCompleto

Caso seja necessrio realizar alguma alterao em qualquer dos atributos ou das tabelas, incluir ou retirar campos, ou mesmo corrigir descries, remova as classes do projeto, faa as alteraes no Genesis, exporte novamente as classes e importe-as novamente. Tudo isto pode ser feito em questo de minutos. Tenha o cuidado de deletar as classes exportadas anteriormente, pois quando elas so sobrepostas e a nova classe for menor que a anterior, o final da primeira no ser apagado, resultando em uma classe com erros no cdigo. Obs: Perceba que os procedimentos testam se o nome do componente iniciado pelo prefixo cls. Logo somente mdulos que se iniciam com o prefixo sero importados ou removidos. Para importar ou remover outros mdulos basta ajustar o prefixo conforme a

118

necessidade. Este modelo apenas foi ajustado para trabalhar com as classes criadas pelo Genesis, cujos nomes se iniciam por cls. Exportando as classes Para gerar e exportar todas as classes de uma s vez clique no boto Exportar Classes (9). A caixa de dilogo do Windows Procurar Pasta ser apresentada. Escolha a pasta para onde deseja exportar suas classes e clique em OK. Caso no ocorram erros uma mensagem ser apresentada ao trmino do processamento informando que as classes foram exportadas com sucesso e exibindo o caminho completo da pasta escolhida. Ateno: Somente as tabelas que estiverem com o campo Criar (4) marcado sero geradas e exportadas. Encerrando o Sistema Para encerrar o Genesis basta clicar no boto de encerramento (27), localizado no canto superior direito da tela principal. Download do Genesis Ser disponibilizado o link para download do Genesis, assim voc poder sempre obter a ltima verso do sistema, contendo as atualizaes mais recentes.
Genesis.zip

Utilizando este sistema com toda certeza seu trabalho de desenvolver projetos orientados a objetos no Access/VBA ser reduzido consideravelmente. Planeje seu modelo de banco de dados, transcreva para o programa, exporte suas classes, gere seu banco de dados e importe as classes. Pronto, seu novo projeto est iniciado. Concluso Depois de aprender a trabalhar com classes e objetos, codificando manualmente todos os atributos e mtodos chega a hora de conhecer uma forma de automatizar esta tarefa. A ferramenta Genesis nos possibilitou esta grande ajuda, poupando-nos tempo e esforo. Vimos como criar tabelas e incluir campos, ajustando seu tamanho, tipo e referncias. Vimos tambm como incluir informaes para a documentao do sistema. Finalmente pudemos verificar como agilizar a incluso ou remoo de mdulos de nosso projeto criado. Todas estas facilidades complementam o conhecimento adquirido durante o aprendizado decorrente dos meses de estudo. Este artigo foi uma pequena adaptao da ajuda contida no programa, resultado de uma reviso geral e a conseqente atualizao da verso anterior do Genesis, incluindo o prprio arquivo de ajuda. E agora, que caminho seguir? Como continuar a jornada do aprendizado?

119

Calma, no prximo artigo, o ltimo da srie, faremos uma abordagem de como utilizar os objetos presentes no MS-Office para aprimorar seus projetos e direcionar suas novas pesquisas. Novas fronteiras sero visualizadas, prontas para serem exploradas. At l ento...

120

Classes X - Concluso
Aps a longa jornada, na qual aprendemos a criar nossos prprios objetos, aplicando-os em nossos projetos baseados em Visual Basic for Applications, chega o momento da despedida. Com isso chega tambm a hora em que o desenvolvedor iniciar sua caminhada solitria rumo ao aperfeioamento de suas habilidades. Como prosseguir? O que devo fazer agora? Como aprofundar e aprimorar o que foi aprendido? Onde buscar novos conhecimentos? Como me atualizar constantemente? Este ltimo artigo da srie tentar responder algumas destas perguntas, com o intuito de abrir a sua mente e fornecer caminhos para que voc possa seguir em frente.

Utilizando os Objetos do Sistema Praticamente todos os itens em um software desenvolvido em Access so objetos. Sendo assim estes objetos podem ser acessados e trabalhados. Conhecendo seus mtodos, propriedades e eventos fica bem mais fcil para o desenvolvedor aproveitar funcionalidades j existentes para construir seus aplicativos. Entre os itens mais utilizados em um sistema Access/VBA esto o formulrio e o relatrio, com seus controles, o recordset e o objeto DoCmd. Alm destes existem dezenas, ou centenas, de outros objetos que podem ser utilizados, mas estes so os exemplos mais comuns e mais simples disponveis. Voc pode realizar qualquer tarefa com um formulrio ou relatrio, bem como dos controles existentes no mesmo, atravs de linhas de cdigo. Para isto basta acessar o objeto. Estando em um mdulo de um formulrio ou relatrio voc pode acessar seus mtodos e propriedades utilizando a palavra chave Me. Como exemplo, depois de alterarmos algum dado na fonte de origem de um formulrio e desejarmos que as alteraes sejam exibidas imediatamente para o usurio, basta realizarmos uma chamada ao mtodo Requery do mesmo para que as informaes sejam atualizadas. Neste caso fazemos a chamada diretamente, sem a necessidade de instanciar o objeto, assim:
'Atualizando o conjunto 'de registros do formulrio Me.Requery

Caso seja necessrio acessar um objeto formulrio a partir de outro mdulo devemos primeiro instanci-lo, pois seus mtodos e propriedades no estaro acessveis antes disso.
'Definindo a varivel de objeto 'para o formulrio. Dim frmCliente As Form 'Definindo a constante de twips 'para clculo de centmetros Const intTwips = 567

121

'Atribuindo o formulrio FCliente ' varivel de objeto formulrio Set frmCliente = Form_FCliente 'Exibindo a legenda do formulrio 'atravs da propriedade Caption MsgBox frmCliente.Caption 'Alterando a propriedade Permitir 'Edio do formulrio frmCliente.AllowEdits = False 'Movendo o formulrio para a posio de 'esquerda a 10cm e superior a 5cm frmCliente.Move 10 * intTwips, 5 * intTwips

Para trabalhar com acesso aos dados armazenados nas tabelas podemos utilizar um objeto do tipo recordset. Veja o exemplo que permitiria buscar os registros da tabela Cliente e exibir o nome de cada cliente.
'Definindo a varivel para 'consulta SQL Dim strSql As String 'Definindo uma nova varivel de objeto 'recordset para conjunto de registros Dim rstCliente As New ADODB.Recordset 'Montando a consulta SQL strSql = "Select * From Cliente" 'Abrindo o recordset rstCliente.Open strSql, CurrentProject.Connection, adOpenStatic, adLockReadOnly 'Exibindo o total de clientes MsgBox "Total de clientes: " & rstCliente.RecordCount 'Exibindo o nome de cada um dos 'clientes existentes no recordset While Not rst.EOF MsgBox "Nome do Cliente " & rstCliente.AbsolutePosition & ": " & _ rstCliente("nomeCliente") rstCliente.MoveNext Wend 'Fechando o recordset rstCliente.Close

Agora vamos supor que voc tem um boto em cada um de seus formulrios cuja funo habilitar a edio dos dados, pois criou todos os formulrios bloqueados para edio. Enquanto a edio estiver liberada o formulrio dever mudar de cor para alertar o usurio que ele est em modo de edio. O cdigo do seu boto seria parecido com este:
Sub btnLiberaEdicao_Click() 'Definindo a cor da seo detalhe para amarelo Me.Section(acDetail).BackColor = vbYellow

122

'Definindo a propriedade permitir edio = true Me.AllowEdits = True 'Atribuindo uma legenda informando que o formulrio 'est liberado para edio Me.Caption = "Ateno: Edio Liberada" End Sub

Este cdigo funcionaria corretamente liberando o formulrio. O problema que voc teria que coloc-lo em todos os botes de liberao de todos os formulrios bloqueados. Como ns j aprendemos o conceito de reutilizao queremos aplic-lo aqui para evitar que qualquer alterao no cdigo tenha que ser feita em mais de um lugar. Ento devemos criar um procedimento genrico que faa o trabalho independente do formulrio a ser liberado, em um mdulo qualquer, onde todo o projeto tenha acesso:
Sub editaFormulario(argForm As Form) 'Definindo a cor da seo detalhe para amarelo argForm.Section(acDetail).BackColor = vbYellow 'Definindo a propriedade permitir edio = true argForm.AllowEdits = True 'Atribuindo uma legenda informando que o formulrio 'est liberado para edio argForm.Caption = "Ateno: Edio Liberada" End Sub

Depois basta chamar o procedimento em qualquer formulrio que necessite ser liberado para edio, passando o formulrio como parmetro para o cdigo:
Sub btnLiberaEdicao_Click() 'Chamando o procedimento que libera 'formulrios e passando como argumento 'o prprio formulrio Call editaFormulario(Me) End Sub

Claro que aps a edio e o salvamento dos dados o formulrio teria que ser bloqueado novamente, mas essa parte deixo como exerccio prtico para voc. O importante perceber que caso voc queira mudar a cor que ser usada para representar a edio do formulrio ter que alterar apenas no procedimento editaFormulario(), j que todos os botes utilizam o mesmo procedimento. Outro objeto bastante utilizado em qualquer projeto com VBA o DoCmd. Este o objeto responsvel por executar as aes do Access a partir de comandos no Visual Basic for Applications. Ele no precisa ser instanciado, pois um objeto ativo, j iniciado com a aplicao. Para utiliz-lo basta chamar um de seus mtodos, passando ou no um parmetro, dependendo da ao desejada:
'Abrindo o formulrio FCliente DoCmd.OpenForm "FCliente"

123

'Maximizando uma tela DoCmd.Maximize 'Fechando uma tela DoCmd.Close

Para aprender a trabalhar com os objetos fornecidos pelo VBA voc pode recorrer documentao acessando-a a partir do prprio editor. Pesquise sobre cada objeto que desejar verificando seus mtodos, propriedades e eventos. Outra maneira ainda mais prtica de pesquisar por estas informaes utilizando um recurso prprio para esta finalidade: o Pesquisador de Objetos. O pesquisador uma ferramenta que facilita bastante a pesquisa por mtodos, atributos e eventos de objetos do sistema. Um ponto importante que ele no se limita a objetos do Access, mas qualquer cdigo que estiver registrado em sua biblioteca, incluindo os objetos de todos os aplicativos do Office. Como exemplo suponha que queremos saber quais so todos os membros do objeto caixa de combinao (combobox). Ao abrir o pesquisador, basta digitar combobox na caixa de pesquisa (1), escolher a classe do objeto combobox (2) que todas as suas propriedades aparecem na lista de membros (3).

Quer saber mais sobre o pesquisador de objetos? Veja o tutorial do amigo Avelino Sampaio: http://www.usandoaccess.com.br/tutoriais/tuto2.asp?id=1#inicio Interagindo com Aplicativos do Office Alm de trabalhar com os objetos internos do Access, o VBA nos permite criar novos objetos ou mesmo acessar objetos existentes de qualquer aplicativo do Office.

124

Imagine que voc queira montar uma planilha no Excel com os dados das vendas do ms, existentes no cadastro do seu banco de dados. Ou talvez gerar cartas de proposta em Word para seus clientes registrados nas tabelas do Access. Mas voc quer que o usurio consiga fazer isto automaticamente, simplesmente clicando em um boto. Ento voc deve utilizar os objetos da classe do aplicativo desejado. O elemento responsvel por criar um objeto a funo CreateObject. Para acessar um objeto existente utilize a funo GetObject. Veja os exemplos:
'Inicia o Microsoft Excel e cria um novo objeto PlanilhadeTrabalho. Set ExcelWorksheet = CreateObject("Excel.Sheet") 'Inicia o Microsoft Excel e abre um objeto PlanilhadeTrabalho existente. Set ExcelWorksheet = GetObject("C:\Planilhas\MinhaPlanilha.xls") 'Inicia o Microsoft Word. Set WordBasic = CreateObject("Word.Basic")

Criando um Objeto Planilha do Excel Para exemplificar a interao com um objeto do Excel vamos criar uma planilha, incluir um texto na clula A1, salvar a planilha em uma pasta do computador e fechar o Excel aps a tarefa.
'Declara uma varivel de objeto Dim minhaPlanilha As Object 'Como a varivel ainda no foi vinculada faremos 'a vinculao tardia. Set minhaPlanilha = CreateObject("Excel.Sheet") 'Torna o Excel visvel atravs do objeto Application. 'Observe que o objeto minhaPlanilha possui um 'atributo que tambm objeto, o Application, que possui 'outras propriedades internas minhaPlanilha.Application.Visible = True 'Coloca um texto na clula A1 da planilha minhaPlanilha.Application.Cells(1, 1).Value = "Esta a coluna A, linha 1" 'Salva a planilha no diretrio C:\MinhaPlanilha.xls 'Obs: Salve como .xlsx no Office 2007 ou superior minhaPlanilha.SaveAs "C:\MinhaPlanilha.xls" 'Fecha o Excel com o mtodo Quit no objeto Application minhaPlanilha.Application.Quit 'Libera a varivel de objeto Set minhaPlanilha = Nothing

Aps criar um objeto voc poder acess-lo novamente utilizando agora a funo GetObject():
'Declara uma varivel de objeto Dim minhaPlanilha As Object

125

'Busca a planilha criada anteriormente 'Obs: Lembre-se que no Office 2007 ou superior o padro .xlsx Set minhaPlanilha = GetObject("C:\MinhaPlanilha.xls") 'Torna o Excel visvel atravs do objeto Application. minhaPlanilha.Application.Visible = True 'Tornando a pasta de trabalho visvel minhaPlanilha.Parent.Windows(1).Visible = True 'Coloca um texto na clula A2 da planilha minhaPlanilha.Application.Cells(2, 1).Value = "Esta a coluna A, linha 2" 'Salva a planilha. minhaPlanilha.Save 'Fecha o Excel com o mtodo Quit no objeto Application minhaPlanilha.Application.Quit 'Libera a varivel de objeto Set minhaPlanilha = Nothing

Veja o resultado:

As regras anteriores so vlidas para qualquer objeto aplicativo diferente daquele em que se est trabalhando. Com isto voc poder usar sua imaginao para realizar, com sucesso, a interao entre aplicativos, buscando, exportando e visualizando informaes da maneira que lhe for mais conveniente. Para saber como programar para cada um dos aplicativos pesquise na documentao que acompanha o sistema. Ali voc encontrar todas as informaes necessrias para compreender e aplicar os conceitos em seus projetos. Alteraes no Modelo de Objetos do Access A cada nova verso de produtos Office vrias alteraes so implantadas. Novos itens so includos, outros so alterados e ainda vrios so removidos.
126

Com relao criao de classes, mtodos e propriedades personalizadas no houve alteraes perceptveis aos usurios em nenhuma das verses, ao logo de todos esses anos. Houve sim vrias modificaes no modelo de objetos do sistema. As tabelas a seguir foram retiradas da documentao do Access 2007, e dizem respeito s modificaes ocorridas comparando-se a verso do Access 2000 com o prprio Access 2007. Escolhi estas tabelas porque foram as mais resumidas que encontrei. Caso fosse apresentar todas as evolues do sistema desde a primeira verso teramos que iniciar outra srie de artigos. Como o nosso foco a criao e utilizao de objetos, deixei de lado o restante e trouxe apenas uma amostra de como pesquisar sobre modificaes das verses. Para encontr-las voc deve abrir a ajuda de qualquer uma das verses, exibir o sumrio e procurar pelo item O que h de novo. O tpico sempre informa as alteraes ocorridas desde a ltima verso. No caso do Access 2007 a prpria Microsoft incluiu um item com o histrico de modificaes desde a verso 2000. Assim, para poupar trabalho, utilizei o que j estava pronto. Isto se chama reutilizao, lembra-se? ;) Resolvi apresentar apenas as modificaes nos objetos mais utilizados pela maioria dos desenvolvedores, ou seja, apenas sero exibidas as alteraes nos objetos mais conhecidos. Voc poder ver a lista completa de modificaes acessando a documentao da ajuda do editor do VBA do Access 2007. AccessObject Propriedades Status Alterado Sintaxe anterior FullName READ-ONLY FullName [STRING] Nova sintaxe READ-WRITE FullName [STRING] Alterado Sintaxe anterior Type READ-ONLY Type [INT32] Nova sintaxe READ-ONLY Type [ACOBJECTTYPE]

AcCommand

127

Propriedades acCmdDataAccessPageBrowse acCmdDataAccessPageDesignView acCmdDemote acCmdImport acCmdMicrosoftScriptEditor acCmdNewObjectDataAccessPage acCmdPromote acCmdPublish acCmdSaveAsASP acCmdSaveAsDataAccessPage acCmdSaveAsIDC acCmdSelectDataAccessPage acCmdShowOnlyWebToolbar acCmdStopLoadingPage acCmdViewDataAccessPages

Status Oculto Oculto Oculto Oculto Oculto Oculto Oculto Removido Oculto Oculto Oculto Oculto Oculto Oculto Oculto

Application Propriedades Status Alterado Sintaxe anterior CurrentObjectType READ-ONLY CurrentObjectType [INT32] Nova sintaxe READ-ONLY CurrentObjectType [ACOBJECTTYPE] Oculto Oculto Oculto Status Alterado Sintaxe anteriorr CONTROL CreateControl (STRING FormName, ACCONTROLTYPE ControlType, OPTIONAL ACSECTION Section = 0, OPTIONAL VARIANT Parent, OPTIONAL VARIANT ColumnName, OPTIONAL VARIANT Left, OPTIONAL VARIANT Top, OPTIONAL VARIANT Width, OPTIONAL VARIANT Height) Nova sintaxe
128

DataAccessPages DefaultWebOptions FileSearch Mtodos

CreateControl

_CONTROL CreateControl (STRING FormName, ACCONTROLTYPE ControlType, OPTIONAL ACSECTION Section = 0, OPTIONAL VARIANT Parent, OPTIONAL VARIANT ColumnName, OPTIONAL VARIANT Left, OPTIONAL VARIANT Top, OPTIONAL VARIANT Width, OPTIONAL VARIANT Height) CreateDataAccessPage Oculto Alterado Sintaxe anterior CONTROL CreateReportControl (STRING ReportName, ACCONTROLTYPE ControlType, OPTIONAL ACSECTION Section = 0, OPTIONAL VARIANT Parent, OPTIONAL VARIANT ColumnName, OPTIONAL VARIANT Left, OPTIONAL VARIANT Top, OPTIONAL CreateReportControl VARIANT Width, OPTIONAL VARIANT Height) Nova sintaxe _CONTROL CreateReportControl (STRING ReportName, ACCONTROLTYPE ControlType, OPTIONAL ACSECTION Section = 0, OPTIONAL VARIANT Parent, OPTIONAL VARIANT ColumnName, OPTIONAL VARIANT Left, OPTIONAL VARIANT Top, OPTIONAL VARIANT Width, OPTIONAL VARIANT Height) Alterado Sintaxe anterior VOID NewCurrentDatabase (STRING filepath) NewCurrentDatabase Nova sintaxe VOID NewCurrentDatabase (STRING filepath, OPTIONAL ACNEWDATABASEFORMAT FileFormat = 0, OPTIONAL VARIANT Template, OPTIONAL STRING SiteAddress = "", OPTIONAL STRING ListID = "") Alterado Sintaxe anterior VOID OpenCurrentDatabase (STRING filepath, OPTIONAL BOOL OpenCurrentDatabase Exclusive = 0) Nova sintaxe VOID OpenCurrentDatabase (STRING filepath, OPTIONAL BOOL Exclusive = 0, OPTIONAL STRING bstrPassword = "")

129

ComboBox Propriedades AllowedText Status Oculto

DoCmd Propriedades Status Alterado Sintaxe anterior VOID ApplyFilter (OPTIONAL VARIANT FilterName, OPTIONAL VARIANT WhereCondition) Nova sintaxe VOID ApplyFilter (OPTIONAL VARIANT FilterName, OPTIONAL VARIANT WhereCondition, OPTIONAL VARIANT ControlName) Alterado Sintaxe anterior VOID OpenReport (VARIANT ReportName, OPTIONAL ACVIEW View = 0, OPTIONAL VARIANT FilterName, OPTIONAL VARIANT WhereCondition) OpenReport Nova sintaxe VOID OpenReport (VARIANT ReportName, OPTIONAL ACVIEW View = 0, OPTIONAL VARIANT FilterName, OPTIONAL VARIANT WhereCondition, OPTIONAL ACWINDOWMODE WindowMode = 0, OPTIONAL VARIANT OpenArgs) Alterado Sintaxe anterior VOID OutputTo (ACOUTPUTOBJECTTYPE ObjectType, OPTIONAL VARIANT ObjectName, OPTIONAL VARIANT OutputFormat, OPTIONAL VARIANT OutputFile, OPTIONAL VARIANT AutoStart, OPTIONAL VARIANT TemplateFile) Nova sintaxe VOID OutputTo (ACOUTPUTOBJECTTYPE ObjectType,
130

ApplyFilter

OutputTo

OPTIONAL VARIANT ObjectName, OPTIONAL VARIANT OutputFormat, OPTIONAL VARIANT OutputFile, OPTIONAL VARIANT AutoStart, OPTIONAL VARIANT TemplateFile, OPTIONAL VARIANT Encoding, OPTIONAL ACEXPORTQUALITY OutputQuality = 0) ; Alterado Sintaxe anterior VOID SelectObject (ACOBJECTTYPE ObjectType, OPTIONAL VARIANT ObjectName, OPTIONAL VARIANT InDatabaseWindow) Nova sintaxe VOID SelectObject (ACOBJECTTYPE ObjectType, OPTIONAL VARIANT ObjectName, OPTIONAL VARIANT InNavigationPane) Alterado Sintaxe anterior VOID TransferSpreadsheet (OPTIONAL ACDATATRANSFERTYPE TransferType = 0, OPTIONAL ACSPREADSHEETTYPE SpreadsheetType = 8, OPTIONAL VARIANT TableName, OPTIONAL VARIANT FileName, OPTIONAL VARIANT HasFieldNames, OPTIONAL VARIANT Range, OPTIONAL VARIANT UseOA) Nova sintaxe VOID TransferSpreadsheet (OPTIONAL ACDATATRANSFERTYPE TransferType = 0, OPTIONAL ACSPREADSHEETTYPE SpreadsheetType = 10, OPTIONAL VARIANT TableName, OPTIONAL VARIANT FileName, OPTIONAL VARIANT HasFieldNames, OPTIONAL VARIANT Range, OPTIONAL VARIANT UseOA)

SelectObject

TransferSpreadsheet

Form Propriedades AllowDesignChanges Status Oculto Alterado Sintaxe anterior READ-ONLY Section [SECTION] (VARIANT Index)
131

Section

Nova sintaxe READ-ONLY Section [_SECTION] (VARIANT Index) Oculto

WhatsThisButton

ListBox Propriedades TextAlign Status Oculto

Report Propriedades Status Alterado Sintaxe anterior Section READ-ONLY Section [SECTION] (VARIANT Index) Nova sintaxe READ-ONLY Section [_SECTION] (VARIANT Index)

TextBox Propriedades AllowedText FELineBreak Status Oculto Oculto

Access em 64 bits Uma das mudanas mais importantes que aconteceu recentemente foi o suporte para aplicativos em 64 bits. Isto denota a chegada irremedivel do futuro do desenvolvimento. Hoje estamos vivendo uma poca de transio em que aplicativos de 32 e 64 bits convivem lado a lado, deixando confusos tanto usurios quanto desenvolvedores. Porm, em um futuro prximo, as aplicaes de 32 bits estaro obsoletas, assim como as aplicaes de 16 bits ainda existentes hoje. Ento porque esperar para aprender sobre
132

esta novidade? Veja o tutorial feito tambm pelo amigo Avelino Sampaio sobre as alteraes necessrias para programar VBA em 64 bits: http://usandoaccess.com.br/tutoriais/tuto28.asp?id=1#inicio Atualizando o Conhecimento Para quem est interessado em se manter atualizado com as novas tecnologias, conceitos e tudo mais que cerca o desenvolvimento com as ferramentas do Office, o principal ponto de referncia o prprio site da Microsoft para desenvolvedores. O MSDN o local onde se encontra toda a documentao sobre os aplicativos, as APIs e as dicas da comunidade. Visite o site e veja quanta informao est disponvel: http://msdn.microsoft.com/pt-br/default.aspx No entanto, h vrios outros locais para se buscar o conhecimento. Na maioria dos sites sobre o assunto voc encontrar artigos, tutoriais e exemplos, alm dos fruns em que participantes mais experientes geralmente tiram as dvidas dos iniciantes, alm das prprias dvidas ao trocar experincias com outros membros. Estes so alguns dos principais: Em Portugus: www.usandoaccess.com.br www.expertaccess.com.br www.ativoaccess.com.br www.juliobattisti.com.br www.apostilando.com www.imasters.com.br/secao/access comunidade.itlab.com.br www.scriptbrasil.com.br www.linhadecodigo.com.br/access.aspx www.marcoratti.net Em Ingls: www.mvps.org/access www.lebans.com www.functionx.com/vbaccess/index.htm
133

www.blueclaw-db.com/accessvisualbasic www.excel-vba.com/excel-vba-contents.htm www.allenbrowne.com/tips.html Alm de participar de fruns e pesquisar em sites, alm da prpria ajuda do Access, extremamente importante ler bons livros sobre o assunto. Procure saber mais sobre orientao a objetos, sobre acesso a dados, modelagem de banco de dados e padres de projeto. Seu aperfeioamento depende agora exclusivamente de voc. Eu ficaria muito feliz em saber que voc decidiu se tornar um desenvolvedor especialista no assunto incentivado pelo que aprendeu nestes artigos. E ficaria mais feliz ainda se soubesse que voc est disseminando este conhecimento para outros interessados. Chegamos ao Fim Enfim chegamos ao final da srie. Foi uma longa jornada em que tive que pesquisar, estudar e planejar meu tempo, buscando janelas de horrios em que pudesse me dedicar a escrever os artigos, enquanto outras tarefas aguardavam ansiosas para seram tambm continuadas. A todos que esperaram com entusiasmo o lanamento de cada um dos artigos peo desculpas pela demora no intervalo entre cada publicao, pois como devem imaginar tudo foi feito nas horas vagas, inclusive finais de semana e feriados. A todos que acompanharam a srie um muitssimo obrigado. Tenho certeza de que as lies foram capazes de proporcionar o conhecimento mnimo necessrio para que desenvolvam projetos orientados a objeto com qualidade. Espero nos encontrarmos em breve em mais um trabalho to quanto ou mais gratificante que este. Abraos e at a prxima!

Fonte: http://www.mabesi.com/busca-avancada.html?searchword=CLASSES&searchphrase=all

134