... Pague apenas R$47,00 uma única vez e tenha acesso por 1 ano aos vídeos e arquivos exemplos do site ...

Clique aqui e obtenha mais detalhes.


Utilizando Classe no Access - As Classes Venda e DatalheVenda

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:

Classe no Access 2007/2010

 

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á...


 

 


14 comentários

Enoque Nunes   15/09/2010 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.

Plinio Mabesi   17/09/2010 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   18/09/2010 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   22/09/2010 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   24/09/2010 08:57:54

Valeu Grande Plínio.

Testei e está tudo certinho dessa vez.

Abraços e fica com Deus.


Enoque Nunes   27/09/2010 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.


Wemerson Bernardo   31/01/2011 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á.

johann   28/05/2013 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/2014 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!

Luciclaudio   09/01/2016 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?

Guidemar   17/03/2016 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.

Silveriovitaliano   28/03/2016 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.

Patrick   30/06/2016 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.

MUITO BOM   11/08/2016 23:38:19

MUITO BOM


Envie seu comentário: