Nesta sexta e última parte da série,
vamos criar os forms para gravar os dados e criar cursores locais
para consultas, etc.
A partir deste ponto, você já poderá adaptar as classes de acesso e consulta de dados que você já tem pronta e as utiliza há muito tempo. Para que isso aconteça, vou criar os forms da forma mais simples possível, não vou colocá-los em uma classe e vou deixar o código dos métodos de consulta e inclusão de forma bem visível.
Para deixar o mais didático possível, não coloquei as regras mais complicadas para validação de dados, veja o código que está dentro de cada formulário e utilize a idéia que estou passando nas classes que você já possui e trabalha diariamente com elas.
É necessário montar o exemplo todo com os códigos já explicados nos artigos passados, pois dentro destes forms faço referência às funções apresentadas anteriormente.
Alguns pontos importantes devem ser considerados no momento em que você for adaptar suas classes que hoje acessam tabelas livres ou banco de dados nativo do VFP:
Você está desenvolvendo um sistema que irá trabalhar com uma grande quantidade de dados.
Muitos usuários irão acessar, alterar e incluir dados, e podem fazer isso através do aplicativo desenvolvido em VFP ou através de qualquer outro aplicativo, como o .NET, VB, ASP, etc.
Não utilize o métodos que tragam todos os dados de uma tabela, principalmente se ela for uma tabela que possua milhares de registros. Com isso você deverá esquecer os famosos botões de "PRÓXIMO", "ANTERIOR", "PRIMEIRO" e "ÚLTIMO" pois eles fazem acesso a dados a todo o momento.
Habitue-se a utilizar transações no lugar de LOCK/UNLOCK.
Crie views no SQL SERVER para facilitar a criação de relatórios, pois as views são instruções SQL que funcionam como se fossem tabelas e são mais rápidas do que você executar uma sentença SQL complexa através do ADO.
Quando for necessário criar um bloco com INSERTs, UPDATEs ou DELETEs em vários registros e em diversas tabelas (por exemplo: Inserir um pedido e seus itens), procure concatenar as strings com todos os comandos e envie tudo apenas uma vez ao banco de dados, assim você executa vários comandos comuns entre si com apenas um acesso ao banco de dados.
Abaixo estão algumas funções utilizadas dentro dos forms, estas rotinas são fundamentais no processo, portanto vamos analisar mais detalhadamente:
Propriedades utilizadas nos forms:
THISFORM.ACAO – Recebe 1 para inclusão ou 2 para alteração.
THISFORM.ID_UNICO – Recebe o ID do registro em processamento.
O Objeto _SCREEN.OMANIPULADADOS é instanciado ao executar o sistema e possui os objetos ADO utilizados para a manipulação dos dados.
Código para inserção ou alteração de dados
Bastante simples, conforme o parâmetro de ação (THISFORM.ACAO) montamos um insert ou então um UPDATE e executamos a string no banco.
LOCAL lcString as String
SET TEXTMERGE on
WITH _screen.omanipuladados
IF thisform.acao = 1 && inclusao
thisform.id_unico = .ObterNovoContador()
TEXT TO lcString noshow
INSERT INTO PRODUTO (ProdID,
ProdNome,
ProdPrecoUnit,
ProdCodBarra)
values ( <<TRANSFORM(thisform.id_unico)>>,
<<thisform.txtnome.Value>>,
<<thisform.txtpreco.Value>>,
<<thisform.txtBarras.Value>>)
ENDTEXT
ELSE && alteração
TEXT TO lcString noshow
UPDATE PRODUTO set ProdNome = <<thisform.txtnome.Value>>,
ProdPrecoUnit = <<thisform.txtpreco.Value>>,
ProdCodBarra = <<thisform.txtBarras.Value>>
where CliID = <<TRANSFORM(thisform.id_unico)>>
ENDTEXT
ENDIF
* Executa string no banco
.IniciarTransacao()
.executar(lcString,3)
IF .lors.errorcount = 0
.EncerrarTransacao
ELSE
.AbortarTransacao
ENDIF
ENDWITH
Código para busca de dados no banco (veja form de cadastro de produtos)
Nesta rotina temos um SELECT que busca os dados dos produtos, após executar o comando verificamos se o recordset possui dados, caso o retorno seja zero, avisamos ao usuário e retornamos FALSE.
Como o usuário está realizando uma busca, aproveitamos para passar a propriedade de AÇÃO para 2 e logo abaixo pegamos os dados do recordset e carregamos os campos do formulário.
LOCAL lcBusca as String. lcString as String
lcBusca = Inputbox("Nome:","Pesquisa")
IF EMPTY(lcBusca)
RETURN .f.
ENDIF
lcBusca = ALLTRIM(lcBusca) + "%"
SET TEXTMERGE on
TEXT TO lcString noshow
SELECT ProdID, ProdNome, ProdPrecoUnit, ProdCodBarra
from PRODUTO
where ProdNome like <<lcBusca>>
endtext
SET TEXTMERGE off
WITH _screen.omanipuladados
.executar(lcString,3)
IF .lors.recordcount <= 0
MESSAGEBOX("Nenhum registro encontrado")
RETURN .f.
ENDIF
thisform.acao = 2 && alterar
thisform.id_unico = .lors.fields("ProdID").value
thisform.txtnome.Value = .lors.fields("ProdNome").value
thisform.txtpreco.Value = .lors.fields("ProdPrecoUnit").value
thisform.txtBarras.Value = .lors.fields("ProdCodBarra").value
ENDWITH
O Formulário um para muitos
Logo no Init do form criamos um cursor local que irá nos auxiliar com os ítens do pedido (lado N).
Na inclusão de dados no relacionamento 1 para N é feito após informar todos os itens do pedido (na grid), para isso montaremos um insert para a tabela PEDIDO e logo abaixo em um SCAN… ENDSCAN montamos os INSERTs para a tabela de itens na mesma variável, observe que há uma linha em branco antes e depois de cada comando de inserção, isto é necessário para que haja uma separação entre os comandos no momento da execução.
Observe que a string é executada apenas uma vez no final do processamento. Desta forma, se houver algum problema com o processo de gravação dos dados (veja a linha IF LORS.ERRORCOUNT = 0) a transação será desfeita. Se gravássemos os dados conforme fôssemos digitando, correríamos o risco de deixar a transação muito tempo aberta, além de aumentar significativamente o tráfego de dados na rede.
* Importante – A variável lcString receberá várias concatenações
* por isso deve haver um espaço em branco antes e depois
* do comando de inserção ou atualização (Veja TEXT / ENDTEXT)
LOCAL lcString as String
SET TEXTMERGE on
WITH _screen.omanipuladados
IF thisform.acao = 1 && inclusao
thisform.id_unico = .ObterNovoContador()
TEXT TO lcString noshow
INSERT INTO PEDIDO ( PedID,
PedData,
PedTotal,
CliID)
values ( <<TRANSFORM(thisform.id_unico)>>,
<<thisform.txtdata.Value>>,
<<thisform.txttotal.Value>>,
<<thisform.id_cliente>>)
ENDTEXT
* Itens do pedido
SELECT itens
GO top
SCAN
lnContador = .ObterNovoContador()
TEXT TO lcString NOSHOW additive
INSERT INTO ITEM (ItemID,
ItemQuant,
ItemValVenda,
PedID,
ProdID)
values (<<TRANSFORM(lnContador)>>,
<<TRANSFORM(itens.Quantidade)>>,
<<TRANSFORM(itens.valUnitario)>>,
<<TRANSFORM(thisform.id_unico)>>,
<<TRANSFORM(itens.IdProduto)>>)
ENDTEXT
ENDSCAN
ELSE && alteração
SELECT itens
GO top
SCAN
TEXT TO lcString NOSHOW additive
UPDATE ITEM SET ItemQuant = <<TRANSFORM(itens.quantidade)>>,
ItemValVenda = <<TRANSFORM(itens.valunitario)>>
where PedID = TRANSFORM(thisform.id_unico)
and ProdID = TRANSFORM(itens.IdProduto)
ENDTEXT
ENDSCAN
ENDIF
* Executa string no banco
.IniciarTransacao()
.executar(lcString,3)
IF .lors.errorcount = 0
.EncerrarTransacao
ELSE
.AbortarTransacao
ENDIF
ENDWITH
Função para busca de dados do pedido
Deixei esta função propositalmente incompleta, pois se você analisá-la mais a fundo, você perceberá que retornará apenas o primeiro pedido do cliente, neste caso você deverá fazer um tratamento no retorno do primeiro select para que o usuário escolha qual pedido será alterado.
Esta rotina é um pouco longa e colocá-la agora nesta função irá desvirtuar a idéia de mostrar a operação básica de busca e gravação dos dados.
Depois disso temos um SELECT que irá buscar os dados do banco e através de um DO… WHILE no recordset, carregamos os dados no cursor de apoio para a GRID.
LOCAL lcBusca as String. lcString as String, lnIdCliente
* Buscar o cliente
lcBusca = Inputbox("Nome do Cliente:","Pesquisa")
IF EMPTY(lcBusca)
RETURN .f.
ENDIF
lcBusca = ALLTRIM(lcBusca) + "%"
SET TEXTMERGE on
TEXT TO lcString noshow
SELECT CliID
from CLIENTE
where CliNome like <<lcBusca>>
endtext
SET TEXTMERGE off
WITH _screen.omanipuladados
.executar(lcString,3)
IF .lors.recordcount <= 0
MESSAGEBOX("Nenhum registro encontrado")
RETURN .f.
ENDIF
* buscar o pedido
.lors.movefirst && move o recordset para o inicio
lnIdCliente = .lors.fields("CliID").value
SET TEXTMERGE on
TEXT TO lcString noshow
SELECT PE.PedID, PE.PedData, PE.PedTotal,IT.ItemQuant, IT.ItemValVenda, PRO.ProdNome
FROM PEDIDO PE, ITEM IT, PRODUTO PRO
WHERE PE.PedID = IT.PedID
and PRO.ProdID = IT.ProdID
and PE.CliID = <<TRANSFORM(lnIdCliente)>>
ENDTEXT
.executar(lcString,3)
IF .lors.recordcount <= 0
MESSAGEBOX("Nenhum registro encontrado")
RETURN .f.
ENDIF
.lors.movefirst && move o recordset para o inicio
thisform.id_unico = .lors.fields("PedID").value
thisform.txtdata.Value = .lors.fields("PedData").value
thisform.txttotal.Value = .lors.fields("PedTotal").value
* Alimentar grid
SELECT itens
ZAP
DO WHILE !.lors.eof()
INSERT INTO itens (idProduto, NomeProduto, Quantidade, ValUnitario);
values (.lors.fields("ProdID").value,;
.lors.fields("ProdNome").value,;
.lors.fields("ItemQuant").value,;
.lors.fields("ItemValVenda").value)
.lors.movenext
ENDDO
thisform.gridProdutos.Refresh
ENDWITH
Espero ter conseguido passar uma visão de como funciona um aplicativo desenvolvido em Visual FoxPro acessando SQL SERVER através de conexão OLEDB/ADO, gostaria de receber de você que está acompanhando esta série de artigos um "feedback" sobre o que você conseguiu fazer ou o que você não conseguiu.
Foi realmente um prazer escrever esta série de artigos e já estou pensando em outro tema para uma próxima série, sugestões serão sempre bem vindas. Obrigado a todos.
Clique aqui para fazer o download dos arquivos.
O post Um aplicativo VFP/SQL SERVER do início ao fim – Parte 06 apareceu primeiro em .