Utilizando Classe no Access - As Classes Venda e DatalheVenda
Nota importante: para ter acesso aos vídeos e arquivos exemplos deste site, adquira um dos planos apresentados abaixo. Você pode comprar em até 5x no Cartão de Crédito, através do Paypal.
Veja como comprar e saiba mais sobre o material oferecido, clicando aqui.

Por: Plinio Mabesi
Estamos chegando na reta final da criação do sistema de vendas orientado a objetos.
Nesta etapa veremos a codificação das classes Venda e DetalheVenda, além da implementação de um PDV que será a nossa interface gráfica para realização das vendas.
No desenrolar da criação dos códigos com certeza você encontrará rotinas repetitivas, funções não tão necessárias e instâncias em excesso. Além 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 orientação a objetos perceberá que um ou outro método poderia ser mais bem implementado de outra maneira. Mas antes de queimar neurônios tentando entender por que o trabalho está assim tão “mal projetado”, lembre-se que tudo foi feito com fins didáticos, e a repetitividade foi proposital, somente para que você fique cansado de tanto manipular objetos e realmente aprenda como utilizá-los.
Não estaremos totalmente livres do aparecimento de “bugs”, pois apenas nos locais estritamente necessários foram inseridas rotinas de tratamento de erros. Estes algoritmos são cansativos de se preparar, então me dei ao luxo de não incluir em todos os procedimentos, funções e métodos do sistema. Porém nada impede que você, que já pesquisou o assunto e agora “saca” muito de programação, implemente ao seu bel prazer, corrigindo erros que porventura se apresentem.
Então chega de papo e mãos à obra...
A Consulta CVenda
Deveremos criar uma consulta que será utilizada por um método da classe Venda. O nome da consulta será CVenda e seu código 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 inclusão, consulta, atualização e exclusão dos objetos do tipo venda.
Este é o código da classe, com todos os seus atributos e métodos, que se utilizam de funções próprias, bem como dos métodos dos objetos da classe ConexaoBD e da classe Cliente.
Código da classe:
Option Compare Database
Option Explicit
'Atributos da Classe
'Atributo de backup e atributo identificador da Classe
'PK - Código que identifica a venda.
Private bkpCodVenda As Variant
Private lngCodVenda As Variant
'FK - Código que indica o cliente relativo à venda.
Private lngCodCliente As Variant
'Objeto da classe de Descrição de Tipo
Private objetoCliente As New clsCliente
'Data de realização da venda.
Private dtmDataVenda As Variant
'Métodos 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
'Método Existe [Com conhecimento de SQL]
'Verifica a existência do objeto Venda na tabela
'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
'Método 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
End Function
'Método 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
'Método Obter [Com conhecimento de SQL]
'Recupera o objeto Venda através 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:
Set rstObter = Nothing
Exit Function
Err_obter:
obter = False
GoTo Exit_obter
End Function
'Método 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 variáveis 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
'Método 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
O objetivo desta classe, conforme dito anteriormente, é oferecer funcionalidades de consulta e atualização dos objetos de ligação entre a venda e os produtos componentes.
Este é o código da classe, com todos os seus atributos e métodos, que se utilizam de funções próprias, bem como dos métodos dos objetos da classe ConexaoBD e da classe Produto.
Código da classe:
Option Compare Database
Option Explicit
'Atributos da Classe
'Atributo de backup e atributo identificador da Classe
'PK - FK - Código que indica o produto relacionado.
Private bkpCodProduto As Variant
Private lngCodProduto As Variant
'Atributo de backup e atributo identificador da Classe
'PK - FK - Código que indica a venda relacionada.
Private bkpCodVenda As Variant
Private lngCodVenda As Variant
'Quantidade do poduto relacionado.
Private dblQtdProduto As Variant
'Métodos 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
End Property
'Método Existe [Com conhecimento de SQL]
'Verifica a existência 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
'Método 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
Exit_incluir:
Exit Function
Err_incluir:
incluir = False
GoTo Exit_incluir
End Function
'Método 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
'Método Obter [Com conhecimento de SQL]
'Recupera o objeto DetalheVenda através 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
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
'Método 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 variáveis 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
'Método getSubTotal
'Calcula o subtotal do produto vendido para a
'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
'Método getListaProduto(argCodVenda As Long)
'Recebe como parâmetro um código de uma venda e devolve
'um conjunto de registros contendo os códigos dos produtos,
'além 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 Funções Auxiliares
Para exibir os itens vendidos na tela, no formato de um cupom fiscal, necessitaremos de uma função que busque os últimos itens registrados na venda para que possamos apresentá-los ao usuário.
Assim sendo deveremos incluir mais uma função no módulo Funcoes criado anteriormente, que será chamada de listaProdutos() e receberá como parâmetros o código da venda e o número 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
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 = " " & FormatNumber(rstLista("qtdProduto"), 3) & _
" x " & FormatCurrency(rstLista("valorUnitario")) & _
" " & FormatCurrency(rstLista("SubTotal")) & _
vbCrLf & listaProdutos
listaProdutos = Format(rstLista("codProduto"), "000000") & " " & _
rstLista("unidade") & " " & rstLista("descricao") & _
vbCrLf & listaProdutos
rstLista.MovePrevious
contador = contador + 1
Wend
End Function
Repare que a função busca os itens começando do final do conjunto de registros, inclusive montando as linhas também do final para o início, até que seja completada a quantidade de itens solicitada, ou todos os itens caso a opção não seja informada.
Ponto Importante
Desta vez, relembrando e aprimorando os conceitos da orientação a objetos, vamos destacar o ponto a seguir.
1. Atributo do tipo objeto: Nos atributos da classe Venda podemos encontrar um que faz referência à classe Cliente, ou seja, o atributo é um objeto da classe Cliente. Por que isto? Apenas adiantando um pouco o assunto do próximo artigo criaremos um modelo de cupom fiscal (bem simples, lógico!!!) no qual esta opção será muito útil. Ao invés de criarmos uma variável de objeto da classe Cliente e a instanciarmos bastará utilizarmos o atributo incluído na classe Venda depois que a mesma for obtida através do código da venda. Neste caso o objeto cliente será instanciado dentro da classe Venda automaticamente assim que o atributo for referenciado. Com isso será possível também acessar todos os atributos e métodos do objeto cliente. Veja os trechos de código, primeiro o método de acesso ao atributo e depois a montagem do cupom fiscal:
Declaração da variável e método de acesso ao atributo:
'Objeto da classe de Descrição de Tipo
Private objetoCliente As New clsCliente
...
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 Gráfica
Desta vez para que possamos visualizar e manipular os dados das vendas resolvi criar um interessante sistema de PDV para ser a nossa interface gráfica, já que há tanta curiosidade acerca dos mesmos.
A tela contará com exibição dos itens registrados em formato de cupom, além do cálculo e registro de produtos utilizando o esquema de digitação do código multiplicado pela quantidade em um único campo. Também faremos a verificação da descrição do produto a partir do código e o cálculo de troco a partir do valor pago, além de outras pequenas funções.
Como não poderia faltar faremos o controle de estoque, tanto baixando itens vendidos como subindo produtos devolvidos, além de verificação de estoque baixo.
A seguir serão definidas as características para o PDV. Assim como nos anteriores as especificações de design também são apenas sugestões, porém as informações de origem de dados, bloqueio de campos e formatos de dados são obrigatórias, sob pena de, novamente, não obter a funcionalidade desejada. Como de praxe as especificações estarão marcadas com um asterisco vermelho ao lado.
No formulário teremos um rótulo que exibirá os produtos vendidos, alguns campos de texto para digitação das informações e os botões 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 padrão serão apresentadas. Para as demais utilize o padrão do sistema, ou o valor que desejar, desde que não comprometa a funcionalidade do sistema.
Formulário
Nome: FVenda Largura: 22cm Altura: Cabeçalho (1,6cm) / Detalhe (15cm) Cor do fundo: Cabeçalho (Vermelho) / Detalhe (Azul escuro) Legenda: Sistema de Vendas Estilo da borda: Fino Seletores de registro: Não Botões de navegação: Não Linhas divisórias: Não Barras de rolagem: Nenhuma Caixa de controle: Não Botão Fechar: Não Botões Min Max: Nenhum Popup: Sim Janela Restrita: Não
Controle Label
Nome: lblProdutos * Largura: 9,7cm * Altura: 10,4cm *
Controles Textbox
Nome: txtCodigoVenda * Formato: 000000 Ativado: Não * Bloqueado: Sim * Cor do fundo: Cinza Alinhamento: Centro Nome: txtData * Formato: dd/mm/yyyy * Cor do fundo: Branco Máscara 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: Não * 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: Não * Bloqueado: Sim * Nome: txtUnidade * Cor do fundo: Cinza Alinhamento: Centro Tamanho da Fonte: 14 Ativado: Não * Bloqueado: Sim * Nome: txtQtd * Cor do fundo: Cinza Formato: Padrão Casas decimais: 3 Alinhamento: Centro Tamanho da Fonte: 14 Ativado: Não * Bloqueado: Sim * Nome: txtValorUnt * Cor do fundo: Cinza Formato: Unidade Monetária Alinhamento: Direita Tamanho da Fonte: 14 Ativado: Não * Bloqueado: Sim * Nome: txtSubTotal * Cor do fundo: Cinza Formato: Unidade Monetária Alinhamento: Direita Tamanho da Fonte: 14 Ativado: Não * Bloqueado: Sim * Nome: txtTotal * Cor do fundo: Laranja Formato: Unidade Monetária Alinhamento: Direita Tamanho da Fonte: 24 Ativado: Não * Bloqueado: Sim * Nome: txtPago * Cor do fundo: Cinza Formato: Unidade Monetária Alinhamento: Direita Tamanho da Fonte: 16 Nome: txtTroco * Cor do fundo: Cinza Formato: Unidade Monetária Alinhamento: Direita Tamanho da Fonte: 16 Ativado: Não * Bloqueado: Sim *
Botões de Comando
Nome: btnExcluirProduto * Legenda: Excluir Produto Cor da Fonte: Vermelho escuro Nome: btnCancelar * Legenda: Cancelar Venda Cor da Fonte: Vermelho escuro Nome: btnNovo * Legenda: Nova Venda Cor da Fonte: Azul escuro Nome: btnFechar * Legenda: Fechar Cor da Fonte: Vermelho escuro
Como sugestão de design os campos deverão estar posicionados conforme demonstrado na figura a seguir, assim como devem ser adicionadas as legendas dos campos, as quais não foram descritas anteriormente:

Códigos do Formulário
Para que nosso formulário seja capaz de manipular os dados dos objetos produto deveremos implementar as funcionalidades necessárias 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 funções genéricas que sejam úteis para quantos procedimentos delas necessitem, reaplicando o conceito da reutilização de código. Além disso, incluiremos os códigos dos campos que possuem eventos, assim como dos botões de comando.
Todos os códigos a seguir deverão ser colocados no módulo do formulário FVenda.
1. Limpeza dos campos: Para efetuar a limpeza dos campos de preenchimento utilizaremos dois procedimentos genérico cuja função será a de atribuir o valor nulo a todos os campos de texto, deixar o rótulo de exibição do cupom em branco e enviar o foco ao campo txtProdutoQtd. Repare que um procedimento reutiliza o código do outro, já que os dois são independentes e o primeiro é utilizado apenas na descrição 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 criação de uma nova venda em branco simplesmente iremos chamar o procedimento limpaCampos() no evento Ao Clicar do botão btnNovo.
Private Sub btnNovo_Click() Call limpaCampos End Sub
3. Cálculo 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 Após 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. Exibição da lista de produtos registrados: Para exibir a lista de produtos registrados sempre que for incluído ou retirado algum produto utilizaremos um procedimento genérico cuja função será a de buscar o lista utilizando a função listaProdutos(), atualizar a exibição do valor total da venda no campo txtTotal e acionar o procedimento que calcula e exibe o valor do troco.
Sub atualizaLista()
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 formulário: No evento Ao Carregar do formulário implementaremos uma pequena rotina que simplesmente atualizará a lista de produtos e moverá o foco para o campo txtProdutoQtd. A atualização da lista só fará sentido quando abrirmos novamente um formulário já carregado anteriormente (por exemplo, quando abrimos o formulário, mudamos para o modo Design e voltamos para o modo Formulário).
Private Sub Form_Load() Call atualizaLista txtProdutoQtd.SetFocus End Sub
6. Criação da venda: Ao iniciarmos uma venda deveremos gerar o código da mesma. Assim criaremos um procedimento genérico que lança mão da função proximoCodigo() para gerar o código da venda, além de atribuir a data atual caso o usuário não tenha informado alguma data específica.
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 código for informado utilizaremos esta rotina no evento Após Atualizar do campo txtCodCliente. Assim será preenchido o campo txtNomeCliente a partir dos dados retornados pelo objeto cliente, ou então um aviso ao usuário informando que o código do cliente é inválido. Ao ser informado um cliente válido a venda também será gerada através 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
Else
txtNomeCliente = Null
txtCodCliente = Null
MsgBox "Código do cliente inválido!", _
vbExclamation, "Erro!"
End If
End If
End Sub
8. Busca do produto em tempo real: O procedimento a seguir permitirá que seja exibida a descrição e a unidade do produto no exato momento em que o código for digitado. Ele será incluído no evento Ao Alterar do campo txtProdutoQtd. Caso o código não corresponda a nenhum produto será exibida uma mensagem no campo txtDescricao informando que o código não está cadastrado. Veja que ele somente será executado completamente caso o valor digitado possa ser interpretado como valor numérico, ou seja, um valor compatível com o código 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 = "Código não 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. Exibição de produto: Para exibirmos todos os dados do produto a ser registrado em uma venda criaremos um procedimento genérico que recebe como argumento um objeto detalhe de venda cujas propriedades e métodos fornecerão as informações para o preenchimento dos campos. Repare que o valor do subtotal é calculado pelo método 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
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 várias verificações, utilizando vários objetos neste procedimento que será incluído no evento Antes de Atualizar do campo txtProdutoQtd para que seja capaz de cancelar o registro caso alguma informação não esteja correta. Primeiro será verificada a validade do código do produto e a correta informação da quantidade, caso seja incluída, atribuindo uma unidade em caso contrário. Em seguida será checada a validade do código da venda e também da data, além do código do cliente, caso seja informado (uma venda sem cliente será considerada venda ao consumidor). Para não ocorrerem conflitos o código da venda será sempre gerado automaticamente, não sendo permitida sua alteração posterior. O próximo passo será verificar se a quantidade solicitada existe em estoque. Caso não possa ser atendida integralmente o registro é cancelado. Existindo a quantidade o produto é registrado, assim como qualquer alteração nos dados da venda. Por último a exibição dos dados é atualizada e o estoque atualizado, informando o usuário se o estoque ficar abaixo do mínimo. Caso ocorram erros durante o processo o esquema de tratamento de erros avisa o usuário sobre qual passo gerou o problema.
Private Sub txtProdutoQtd_BeforeUpdate(Cancel As Integer)
On Error GoTo Err_txtProdutoQtd_AfterUpdate
Dim codigoProduto As Long
Dim qtdProduto As Double
Dim posicao As Integer
Dim status As Integer
Dim objVenda As New clsVenda
Dim objDetalhe As New clsDetalheVenda
Dim 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
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 não é 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 mínima!" & _
vbCrLf & vbLf & _
"Estoque atual: " & FormatNumber(objProduto.qtdEstoque, 3) & _
" " & objProduto.unidade, vbExclamation, "Estoque Baixo"
End If
Else
MsgBox "Ocorreu um erro na atualização 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:
Exit Sub
Err_txtProdutoQtd_AfterUpdate:
Select Case status
Case 1, 3
MsgBox "Código do produto inválido!", vbExclamation, "Erro!"
Case 2
MsgBox "Quantidade do produto inválida!", vbExclamation, "Erro!"
Case 5
MsgBox "Data inválida!", vbExclamation, "Erro!"
Case 6
MsgBox "Código do cliente inválido!", 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 não é um sistema real. Sendo assim os códigos 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 transações para garantir que todos os processos ocorressem integralmente, do início ao fim, ou então que todas as etapas fossem canceladas e os dados retornassem ao estado original. Não poderíamos, por exemplo, ter uma venda registrada sem que o estoque fosse atualizado. Mas isto é outra história...
11. Atualização das informações: Após o registro de um produto as informações exibidas devem ser atualizadas e o foco retornar para o campo de digitação de produto e quantidade, a fim de que ele esteja pronto para efetuar novo registro. Para executar esta tarefa criaremos um procedimento no evento Após Atualizar do campo txtProdutoQtd.
Private Sub txtProdutoQtd_AfterUpdate() btnNovo.SetFocus txtProdutoQtd.SetFocus txtProdutoQtd = Null Call preencheVenda Call atualizaLista End Sub
12. Exclusão 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 botão btnExcluirProduto. Ele será encarregado de solicitar ao usuário o código do produto que deverá ser excluído, checando sua validade, atualizando o estoque e finalmente excluindo o produto, sendo que os dados de exibição devem ser novamente atualizados através de uma chamada ao procedimento genérico atualizaLista(). Repare que não 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 código do produto a ser excluído:", _
"Exclusão 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 excluído com sucesso!", _
vbInformation, "Exclusão de Produto"
Call atualizaLista
Else
MsgBox "Ocorreu um erro durante a exclusão do produto!", _
vbExclamation, "Exclusão de Produto"
End If
End If
End If
End If
Else
MsgBox "Código de produto inválido!", _
vbExclamation, "Exclusão de Produto"
End If
End If
End Sub
Obs: Veja que não incluí tratamento de erro neste procedimento. Também não incluí e nem incluirei em vários outros. Esta tarefa deverá ser executada no evento Ao Praticar do objeto LeitorAplicado.
13. Cancelamento da venda: Assim como podemos excluir um produto registrado também 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 responsável será incluído no evento Ao Clicar do botão btnCancelar. O cancelamento deverá contemplar a busca do conjunto de registros dos produtos da venda, através do método getListaProduto() do objeto detalhe de venda, que serão verificados um a um para retorno ao estoque. Em seguida a venda será excluída, ocasionando a exclusão dos registros associados na tabela DetalheVenda pelo mecanismo do relacionamento em cascata. Após a exclusão os campos serão limpos para iniciar nova venda.
Private Sub btnCancelar_Click()
Dim objVenda As New clsVenda
Dim objDetalheVenda As New clsDetalheVenda
Dim objProduto As New clsProduto
Dim 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 formulário no evento Ao Clicar do botão 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.
Continua a recomendação de consultar o exemplo apenas para tirar dúvidas e verificar como foi feito, além de ver o sistema em funcionamento. Treinem bastante, digitem, testem, façam o cérebro pedir água. Esta é a única maneira de realmente assimilar o conhecimento.
Segue o link para download:
Caso ainda não esteja com uma tela principal abra o formulário FVenda diretamente na janela Banco de Dados (Access 2003-) ou no Painel de Navegação (Access 2007+).
Conclusão
Depois desta exaustiva etapa foi possível percebermos o quanto os objetos devem interagir para chegar ao fim de uma tarefa. Funções, procedimentos, eventos, métodos e atributos trabalham em conjunto para produzir resultados que atendam as necessidades do usuário.
Este artigo foi dedicado a demonstrar de maneira maçante a criação, utilização e troca de informações entre objetos em meio aos códigos do restante do sistema, por isso mesmo foi bem mais longo que os demais.
Além de toda a programação em si foi possível 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 implementação: o PDV. Mas nosso sistema de vendas ultramoderno, com tecnologia orientada a objetos, não estaria completo sem ele, não é mesmo?
Aqui praticamente encerramos a parte mais trabalhosa. De agora em diante veremos apenas a finalização do sistema, algo mais estético do que funcional, além de um pouco mais teoria nos artigos seguintes, com a apresentação do Genesis, a ferramenta case para VBA que facilitará a sua vida, e a abordagem sobre utilização de objetos no MS-Access para aprimorar seus projetos.
Conto com a presença de todos na próxima etapa, pois esta é a razão de ser desta série de artigos.
Até lá...
Artigos Relacionados
Utilizando Classe no Access - Introdução
Utilizando Classe no Access - Parte 1 - Orientação a Objetos
Utilizando Classe no Access - Parte 2 - Programação OO no Access/Vba
Utilizando Classe no Access - Parte 4 - As Classes Auxiliares
Utilizando Classe no Access - Parte 5 - A Classe Cliente
Utilizando Classe no Access - Parte 6 - A Classe Produto
Utilizando Classe no Access - Parte 7 - As Classes Venda e Detalhe de Venda
Utilizando Classe no Access - Parte 8 - Finalização do Sistema
Utilizando Classe no Access - Parte 9 - Genesis: A Ferramenta Case
Utilizando Classe no Access - Parte 10 - Conclusão
Como estudar com o Pesquisador de Objetos
|
14 comentários MUITO BOM 11/08/2022 23:38:19 MUITO BOM Patrick 30/06/2022 23:46:04 Boa noite. Gostaria de dizer que este site é 10! Sempre nos ajudando a assimilar os vários usos do access e facilitando a vida de iniciantes. Estive analisando o conteúdo do tutorial e fiquei impressionado, porém, não estou conseguindo trabalhar encima do código do FVenda. Nos meus testes não consigo inserir novos campos e salvar os mesmos, gravando seus dados na tabela. Exemplo: Gostaria de criar um campo de seleção com códigos de vendedor que eu mesmo determinaria, até consegui criar a combobox porém não consigo determinar a variante que faça os dados selecionados serem gravados na tabela Venda. Sempre fica em branco. Como poderia proceder para incluir um novo campo de seleção e gravar os dados selecionados no momento em que estou gravando os da venda? Grato. Silveriovitaliano 28/03/2022 18:58:25 Baixei e fiz alguns testes. Posso garantir que ficou muito bom. As mudanças que se assim acharem necessários...serão meramente detalhes da OBRA FEITA. Guidemar 17/03/2022 10:42:32 Bom dia Plinio, parabéns ótimo sistema, queria ver se você pode me ajudar quero montar um sistema para lancheria, quero que o sistema desconte do estoque os acompanhamentos do lanche, exemplo vendo um bauru, além de descontar o pão do estoque, desconte também o presunto queijo e demais ingredientes, desde já agradeço. Luciclaudio 09/01/2022 04:21:48 Galera tenho uma duvida, quando tento converter um texto com mais de 11 dígitos no access tenho o seguinte erro CODIGO convertido CODIGO_normal 1,38055651547E+11 138055651547 8,71651000905E+11 871651000905 o que posso fazer para torna-lo numero? johann 28/05/2021 19:55:08 Boa noite plinio, meu nome é johann e eu gostei do seu resultado sobre o pdv mostrado no seu site e dei uma melhorada colocando senhas e logins, permissoes de usuarios e entre outros pra transformar em um verdadeiro sistema mais tenho uma duvida, se vc poder me ajudar eu serei muito grato. Eu queria colocar ao inves de codproduto*qtdproduto na tela de venda codigoDeBarra*qdeProduto ou se podesse digitar codigo do produto ou codigo de barra x a quantidade do produto e ele funcionasse. Porque aonde eu trabalho tanto eu digito o codigo do produto como o codigo de barra. Obrigado e fico no seu aguardo. Gil Kléber 16/04/2021 16:55:58 Olá Plínio! Parabens pela atitude de deixar seu sistema disponivel para download! Me ajudou muuito!!! No entanto, há algo (que para vc deve sem bem simples de fazer) que não consigo fazer: Coloquei uma caixa de combinação no formulário FVenda, para selecionar 2 vendedores. Consegui fazer com que aparecesse no cupom fiscal sem problemas, mas não consigo salvar na tabela Vendas. (preciso salvar esse campo e o campo TOTAL, pq faço calculos de comissão no final do mês). Pode me ajudar? Mais uma coisinha... não consigo adicionar 2 produtos do mesmo código e isso será recorrente aqui na minha lojinha... como posso fazer?? Grande abraço e mais uma vez, obrigado! Enoque Nunes 27/09/2020 13:52:30 E ai Plínio, beleza? Cara, me diz uma coisa, o problema todo estava sendo causado apenas pelo sinal de "=" na linha: If dblQtdEstoque - Abs(argQtd) <= 0 Then ? Foi a única coisa que alterei no exemplo anterior para poder funcionar normalmente, depois disso funcionou sem problemas, até arrisquei colocar de volta o sinal de "=" para ver o que ia acontecer e para minha surpresa funcionou sem erros. Não sei qual foi a jogada, mas funcionou. Abraços amigão e até a próxima. Enoque Nunes 24/09/2020 08:57:54 Valeu Grande Plínio. Testei e está tudo certinho dessa vez. Abraços e fica com Deus. Plinio Mabesi 22/09/2020 20:02:04 O erro está no método baixarEstoque() da classe Produto. O código deve ser alterado para este: 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 Como o método considerava como falso resultados menores ou iguais a zero o estoque nunca reduzia a quantidade para abaixo de 1 unidade. Deixando falso apenas os valores menores que zero tudo corre normalmente. Mas lembrem-sem que isto não quer dizer que não haja outros erros. Agradeço por novas informações. Valeu Enoque... Enoque Nunes 18/09/2020 09:02:47 Cara, como é que você faz uma coisa dessas, vir aqui e não me avisar? rsrsrsrs, iria lhe convidar para comer um delicioso churrasco de tambaqui. Mas, oportunidades acredito que não irão faltar. Abraços amigão e fica na paz do Senhor Jesus. Até mais. Plinio Mabesi 17/09/2020 18:01:55 Olá Grande Enoque... Estive aí na sua terrinha semana passada, mas foi uma passagem rápida. Vou checar o programa e assim que corrigir peço para o Avelino postar a correção. Valeu... Enoque Nunes 15/09/2020 13:25:27 E ai grande mestre Plínio, beleza meu amigo? Cara, você está de parabens mesmo pelo artigo, baixei seu segundo exemplo e pasme, achei fantástico, sempre quis fazer algo semelhante e não sabia nem por onde começar. rsrsrsrs Mas, vamos lá. Estive explorando ontem à noite seu PDV e acredito que encontrei um o erro. Veja só. Cadastrei um produto e dei entrada de 05 peças, fui dando baixa um a um, até ai tudo bem, o problema veio depois que as 05 peças tinham acabado e o PDV continuou aceitando baixa e sem zerar o estoque, ele fica parado sempre com uma peça no estoque e sem abrir mensagem de estoque zero. Assim, lancei várias saidas no PDV com uma única peça de saldo no estoque, eu tinha dado entrada apenas de 05 peças. Dá uma checada ai amigão. Abraços e fica com Deus. Wemerson Bernardo 31/01/2020 13:05:31 Olá, Sei que já contrinbuiu demais com o bd disponibilizado (VendaOO). Confesso que segui suas dicas, estou estudando bastante o Gênesis, e as dicas no site. Mas como não entendia muito de VBA, até assimilar tudo pode ser que demore um pouco, e ainda assim continuo com dúvida. Como vc me orientou, gerei os códigos com o gênesis e não consegui ainda. Primeiro acrescentei o campo codBarra como string, coloquei como chave primária tb, o acrescentei na tab DetalheVenda, fiz as relações e nada. Sem sucesso, tentei apenas mudar as propriedades do campo existente codProduto para texto. (Para caber a qtd de digitos) Gerei o código com o gêneses e comparei linha por linha com o do projeto original (e não vi nada diferente). Então. Acho que o erro está no evento AO ALTERAR do campo txtProdutoQtd sempre que vou digitar mostra o seguinte erro: Mesmo o produto estando cadastrado na tabela Produto informa: Código não Cadastrado e também "Erro SQL: Select*From Produto Where codProduto=1 (= a Um ou o 1º nº digitado) Outro caso é a linha if is numeric, acho que deveria alterar, já que o campo passou a ser texto, não? Enfim, gostaria muito dessa ajuda. ---------------- 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 = "Código não cadastrado..." txtUnidade = Null End If txtQtd = Null txtValorUnt = Null txtSubtotal = Null End If End If Else txtDescricao = Null txtUnidade = Null End If End Sub No evento antes de atualizar Private Sub txtProdutoQtd_BeforeUpdate(Cancel As Integer) On Error GoTo Err_txtProdutoQtd_AfterUpdate Dim codigoProduto As Long não sei se continua como long ou Dim codigoProduto As String Mais uma vez, Desculpas e Grato desde já. |

