Clique aqui para ler este artigo em pdf
Clique aqui para ler todos os artigos desta edição
Controle de Estoque no Pocket PC: do eVB ao VB .NET
por Leandro Machado
Criar projetos para dispositivos móveis é desafiador. Por um lado você tem todo o poder de um Pocket PC nas mãos, e por outro os dados que estão no servidor. Então, como se comunicar com o banco de dados no servidor e usar as funcionalidades do Pocket PC para desenvolver um projeto de coletores de dados para controlar o estoque? O objetivo deste artigo é ensinar como fazer isso usando a linguagem VB.NET e o bando de dados SQL Server. Em alguns momentos, farei uma comparação entre o eVB (Embebbed Visual Basic) e o VB.NET (Visual Basic .NET).
Inicialmente, eu precisava escolher uma linguagem de desenvolvimento para a aplicação. A Microsoft oferecia duas opções de linguagens para o desenvolvimento Embedded: o C e o modelo visual , o eVB. A opção pela linguagem baseada em visual foi natural, na medida em que toda a equipe de desenvolvimento já a utilizava para o desenvolvimento de aplicações Desktop e uma vez que o custo de aprendizado poderia ser minimizado com essa escolha.
Primeiramente, desenvolvi uma aplicação Off-line sincronizando os dados pelo ActiveSync. Os motivos: o alto custo em equipamentos para conectar os dispositivos à rede e, principalmente, a complexidade de codificar a aplicação para acessar diretamente o servidor SQL Server.
Com o lançamento do Visual Studio.Net 2003 e com a popularização da tecnologia Wi-Fi, optei novamente por trocar a plataforma de desenvolvimento. Essa decisão foi a mais fácil de minha carreira profissional, haja vista as incomparáveis facilidades proporcionadas pelo Visual Studio.Net.
A seguir, demonstrarei como fiz a primeira migração de um aplicativo desenvolvido em eVB para a plataforma .Net. Essa migração demandou apenas um dia de trabalho. Vale ressaltar que levei esse tempo porque separei algumas horas para ler o Help do programa.
Conferência de Estoque
O aplicativo em questão é simples e fácil de entender. Uma rotina do departamento de Expedição da empresa é a conferência física do estoque. No passado, o relatório de estoque era impresso em intermináveis páginas, as quais eram divididas entre os conferentes, que saíam em campo conferindo o que estava na lista. Quando encontravam algum item com uma quantidade diferente da especificada na lista, anotavam essa diferença na própria lista. O procedimento, além de muito demorado, gerava muitas dúvidas, e nunca se tinha certeza de que tudo havia sido conferido corretamente. O sistema de conferência pelo Pocket proporcionou maior velocidade e confiabilidade ao procedimento de conferência de estoque.
Tornando a aplicação On-Line
A grande mudança proporcionada pela troca da plataforma para o VS.Net consistiu em possibilitar o acesso a dados On-Line pela aplicação. A tecnologia utilizada foi a Web Services. Foi criado e publicado na Intranet da empresa um Web Service, o qual será consumido pela aplicação que roda no Pocket PC através da rede local, usando Wi-Fi.
No Web Service, foi criada uma função que retorna um DataSet que poderá ser usado em diferentes pontos do projeto. Como a aplicação trabalha desconectada, o Web Service é invocado no servidor, conecta a base de dados, monta um DataSet, retorna os dados à aplicação e se desconecta. Veja na Listagem 1 o código do Web Service com a função Pesquisa, o qual recebe como parâmetro uma variável do tipo String com a instrução SQL ou com a Stored Procedure desejada e, em seguida, processa e retorna um DataSet à aplicação.
Listagem 1 Web Service com a função Pesquisa
_
Public Function Pesquisa(ByVal xSql As String) As DataSet
‘Coloque a string de conexão com seu banco de dados
Dim conexao As String =”String de conexão”
Dim conn As SqlConnection
Dim da As SqlDataAdapter
Dim ds As DataSet
conn = New SqlConnection(conexao)
Try
conn.Open()
da = New SqlDataAdapter(xSql, conn)
ds = New DataSet
da.Fill(ds, "Tabela")
Return ds
Catch ex As Exception
Throw ex
Finally
conn.Close()
End Try
End Function
No Web Service, também foi criado uma Sub para operações de Insert, Update e Delete, que recebe como parâmetro uma variável do tipo String com a instrução SQL ou com a Stored Procedure desejada e processa a instrução. Caso ocorra algum erro durante o processamento da instrução, a Sub retornará o erro por meio da linha Throw ex (veja a Listagem 2).
Listagem 2 Sub para operações de Insert, Update e Delete
_
Public Sub FazRSsr(ByVal xSql As String)
‘Coloque a string de conexão com seu banco de dados
Dim conexao As String = “String de Conexão”
Dim myConnection As New SqlConnection(conexao)
myConnection.Open()
Dim sql As String = xSql
Dim myCommand As New SqlCommand(sql, myConnection)
Dim myTrans As SqlTransaction
myTrans = myConnection.BeginTransaction()
myCommand.Connection = myConnection
myCommand.Transaction = myTrans
Try
myCommand.ExecuteNonQuery()
myTrans.Commit()
Catch ex As Exception
myTrans.Rollback()
Throw ex
Finally
myConnection.Close()
End Try
End Sub
Com o Web Service criado, o próximo passo é publicar no servidor com o IIS (Internet Information Server) da Intranet, já que o consumo desse Web Service seria feito apenas internamente, pela rede local.
No eVb o acesso a dados era feito por ODBC. Criava-se uma fonte de dados ODBC e usava-se o Microsoft ActiveSync para importar essa fonte de dados para o Pocket PC, gerando assim uma base de dados local no Pocket. A cada conexão do dispositivo à base, o ActiveSync cuidava de replicar os dados do Pocket com a base SQL Server. Essa sincronização eventualmente gerava conflitos, e nossos analistas eram obrigados a resolvê-los manualmente.
Criando uma nova aplicação
Abra o VS.Net 2003, clique em New Project ou pressione CTRL + SHIFT + N. Em Visual Basic Projects, selecione SmartDeviceApplication, escolha o nome do seu projeto e o local onde irá gravá-lo. Pressione OK e, na janela aberta, selecione em “What Platform do you want to target?” a opção “Pocket PC”. Por fim, clique no botão OK.
A Interface
A Toolbox do .Net é muito ampla e a quantidade de objetos e classes disponíveis é infinitamente maior do que no eVB, mas uma de minhas preocupações durante a migração foi manter o mesmo layout da interface, pois os usuários do sistema já estavam habituados com o layout desenvolvido em eVB. Mesmo optando por manter o layout, os ganhos de produtividade no desenvolvimento foram muitos e vocês verão agora o porquê.
Na Toolbox, selecione o objeto InputPanel, que será usado para mostrar um teclado na tela do Pocket PC de modo a permitir que o usuário digite os dados. Use o código da Listagem 3 para habilitar ou desabilitar a visualização do teclado.
Listagem 3 Habilita ou desabilita a visualização do teclado
Private Sub txt_NumeroOrdem_GotFocus( _
ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txt_NumeroOrdem.GotFocus
InputPanel1.Enabled = True
End Sub
Private Sub txt_NumeroOrdem_LostFocus( _
ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txt_NumeroOrdem.LostFocus
InputPanel1.Enabled = False
End Sub
Em uma aplicação para Pocket PC, não é aconselhável o uso de muitos Forms para racionalizar o uso de memória. Uma boa saída para isso é o uso de TabControls, onde o programador pode inserir um desses objetos na tela, ajustá-lo ao tamanho da tela e usar quantos tabs forem necessários para a aplicação, economizando assim o uso de Forms. Veja na Figura 1 o formulário com os tabs criados.
Figura 1 Telas da aplicação
|
|
|
|
De cara notei uma grande mudança em relação ao eVB. Embora o recurso do objeto TabControl existisse no eVB, o gerenciamento da visualização dos Tabs precisava ser feito manualmente. Para cada Tab, era necessário o uso de um frame, o que reduzia ainda o espaço disponível na tela do Pocket. Dentro de cada Frame eram colocados os objetos. Tanto em ambientes de desenvolvimento como em execução, o evento click nas Tabs não executa nada, ou seja, o desenvolvedor é quem deve disparar uma rotina do tipo Frame1.Viseble=true e Frame2.Viseble=False.
No .Net esse gerenciamento é feito pelo próprio controle, e toda a codificação necessária no eVB torna-se desnecessária. Com o objeto inserido e dimensionado na tela, na barra Properties, clique na propriedade TabPages para exibir a janela de configuração TabPage Collection Editor, onde iremos criar e configurar cada Tab da página. No exemplo, utilizei Quatro Tabs (veja a Figura 1).
Na Tabpage1, serão exibidas informações das conferências geradas na Aplicação Desktop, que trabalha em conjunto com a aplicação do Pocket PC. Foram usados um DataGrid e um Label. Na Tabpage2, serão exibidos os dados da pesquisa de cada item de estoque a ser pesquisado e conferido. Foram usados nove TextBox, um Label para cada TextBox, quatro Buttons e um VScrollBar. Na Tabpage3, serão exibidos em um Grid os itens já conferidos. Foram usados dois Buttons, um DataGrid e um Label. Na Tabpage4, serão exibidos os itens conferidos, porém com dados físicos diferentes dos dados que constam na base SQL Server, denominados “Dúvidas”. Foram usados dois Buttons, um DataGrid e um Label.
Códigos
Antes de tudo precisamos fazer referência ao WebService que iremos consumir. Abra a janela do Solution Explorer, clique com o botão direito no nome do projeto e selecione Add Web Reference. Digite a URL completa do local (http://servidor/projeto/pagina.asmx) onde está publicado seu Web Service e use o botão GO para confirmar a existência deste. Em seguida, é só clicar em Add Reference.
Pressione F7 para abrir a janela de código e defina as seguintes variáveis globais:
Option Explicit
'código da conferência selecionada
Dim cellValue As Integer
'Código do Num de ordem conferida selecionado
Dim cellConf As Integer = 0
O Tabpage1 é o padrão e é nele que o usuário deve selecionar a conferência de estoque com a qual pretende trabalhar. Essas conferências são geradas na aplicação Desktop pelos responsáveis por cada estoque da empresa. Como a empresa possui diversos estoques diferentes, o DataGrid colocado nesse Tabpage serve para listar os códigos de todas as conferências geradas no sistema Desktop. No evento Load do Form, digite o código da Listagem 4.
Listagem 4 Código do Load do formulário
Private Sub Conferencia_Frm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
Cursor.Current = Cursors.WaitCursor
CarregaGridConferencias()
Cursor.Current = Cursors.Default
End Sub
Private Sub CarregaGridConferencias()
'Configura o estilo de formatação do grid
'A propriedade MappingName = "Tabela" é devido ao
'retorno .Tables("Tabela") do WebService
Dim style As New DataGridTableStyle
style.MappingName = "Tabela"
'Para cada coluna do grid, que deseja formatar
'deve-se criar um DataGridTextBoxColumn
Dim tcol As New DataGridTextBoxColumn
With tcol
.HeaderText = "Conferência"
.MappingName = "ConfCodigo"
.Width = 100
End With
Dim tcol2 As New DataGridTextBoxColumn
With tcol2
.HeaderText = "Data"
.MappingName = "ConfData"
.Width = 100
End With
'Adicione os DataGridTextBoxColumn ao
'DataGridTableStyle
style.GridColumnStyles.Add(tcol)
style.GridColumnStyles.Add(tcol2)
'Adicione os DataGridTableStyle ao DataGrid
dtg_Conferencia.TableStyles.Add(style)
'Instancia o WebService e carrega o grid
Try
Dim WS As New WebReference.Service1
dtg_Conferencia.DataSource = WS.Pesquisa("sp_PDA_Conferencia").Tables("Tabela").DefaultView
Catch ex As Exception
Msgbox("Erro : " & ex.Message, MsgBoxStyle.Critical, "ERRO!")
End Try
End Sub
Note que na rotina CarregaGridConferencias() a maior parte da codificação trata apenas da formatação do DataGrid, tanto das linhas quanto das colunas. Usei a classe DataGridTableStyle porque ela facilita a formatação e exibição dos dados no Grid.
Os dados do banco de dados SQL Server são carregados no DataGrid através do consumo do Web Service criado no início do artigo. No consumo do Web Service, usei a estrutura Try/Catch para tratar o erro e, se não fosse esse tratamento, a codificação estaria resumida a duas linhas.
Dim WS As New WebReference.Service1
dtg_Conferencia.DataSource = WS.Pesquisa("sp_PDA_Conferencia").Tables("Tabela").DefaultView
O parâmetro do Web Service é o nome de Stored Procedure (veja a Listagem 5), mas seria possível passar qualquer código SQL.
Listagem 5 Stored Procedure
CREATE Procedure sp_PDA_Conferencia
as
Select ConfCodigo, ConfData
From ConfereEstoque
GO
No eVb não existe o DataGrid, mas sim algo parecido com o MSFlexGrid. Esse recurso era outro grande gerador de código, pois a formatação e a população do Grid eram feitas manualmente, linha por linha, coluna por coluna, célula por célula. Tudo manualmente. É necessário dizer que esse método manual de popular o Grid torna o carregamento dos dados extremamente lento, devido ao fato de o código processar individualmente todas as linhas do Recordset gerado para a tarefa.
Como os dados estão exibidos no GRID, o usuário precisa selecionar a conferência desejada usando a caneta no Grid que contém a conferência em que irá trabalhar (veja a Listagem 6).
Listagem 6 Seleciona um item no DataGrid
Private Sub dtg_Conferencia_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles dtg_Conferencia.Click
Cursor.Current = Cursors.WaitCursor
Try
LimpaCampos(TabPage2.Controls)
' Valor que guarda a Linha selecionada
Dim vIntRow As Integer
vIntRow = dtg_Conferencia.CurrentRowIndex
'Seta a seleção para a Coluna 0, que é a coluna
'do código. A linha da seleção é mantida na
'variável vIntRow
dtg_Conferencia.CurrentCell = _
New DataGridCell(vIntRow, 0)
Dim selectedCell As DataGridCell
selectedCell = dtg_Conferencia.CurrentCell
Dim selectedItem As Object
selectedItem = _
dtg_Conferencia.Item(selectedCell.RowNumber, _
selectedCell.ColumnNumber)
cellValue = CInt(selectedItem)
'Preenche o Label com o Código da Conferencia
'selecionado no grid
lbl_Codigo.Text = "Código Selecionado:" & _
cellValue.ToString
Me.Text = "Conferência:" & cellValue.ToString
Catch ex As Exception
Msgbox("Erro : " & ex.Message, MsgBoxStyle.Critical, "ERRO!")
Finally
Cursor.Current = Cursors.Default
End Try
End Sub
Selecionado o Código da Conferência, nossa Tab de trabalho passa a ser a Tabpage2. Nessa Tab, o usuário digita o número de ordem que identifica o item a ser conferido. Quando ele selecionar o Botão “Pesquisa”, será feito um novo consumo ao Web Service para carregar as características do item na tela Pocket PC (veja a Listagem 7).
Listagem 7 Código do botão Pesquisa
Private Sub btn_Pesquisa_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btn_Pesquisa.Click
Cursor.Current = Cursors.WaitCursor
If cellValue = 0 Then
Msgbox("Você não selecionou nenhum Código de Conferência!", MsgBoxStyle.Information, "Atenção!")
Cursor.Current = Cursors.Default
Exit Sub
ElseIf IsNumeric(txt_NumeroOrdem.Text.Trim) = False Then
Msgbox("Você não digitou números válidos!", MsgBoxStyle.Information, "Atenção!")
Cursor.Current = Cursors.Default
Exit Sub
Else
MoveCampos()
Cursor.Current = Cursors.Default
End If
End Sub
Sub MoveCampos()
Try
Dim WS As New WebReference.Service1
Dim dts As New DataSet
dts = WS.Pesquisa("sp_PDA_PesquisaSubOrdem " & cellValue & "," & CInt(txt_NumeroOrdem.Text))
If dts.Tables("Tabela").Rows.Count = 0 Then
Dim vStrMsg As String = _
Msgbox("Produto não encontrado!Deseja Marcar como Dúvida?", MsgBoxStyle.YesNo, "Atenção!")
If vStrMsg = MsgBoxResult.No Then
Exit Sub
Else
HabilitaText(TabPage2.Controls)
txt_Ordem.Text = txt_NumeroOrdem.Text.Trim
txt_Status.Enabled = False
btn_Confirma.Enabled = False
btn_SalvaDuvida.Text = "Salvar Dúvida!"
End If
Else
LimpaCampos(TabPage2.Controls)
txt_NumeroOrdem.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubOrdem"))
txt_Descricao.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubDescricao"))
txt_Diametro.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubDiametro"))
txt_Formato.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubFormato"))
txt_Gm2.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubGramatura"))
txt_Largura.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubLargura"))
txt_Ordem.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubOrdem"))
txt_Quantidade.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubQuantidade"))
txt_Status.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "ConfSimNaoDuvida"))
If txt_Status.Text = "C" Then
txt_Status.Text = "CONFERIDO"
ElseIf txt_Status.Text = "D" Then
txt_Status.Text = "DÚVIDA"
ElseIf txt_Status.Text = "N" Then
txt_Status.Text = "NÃO CONFERIDO"
End If
End If
Catch ex As Exception
Msgbox("Erro : " & ex.Message, MsgBoxStyle.Critical, "ERRO!")
Finally
Cursor.Current = Cursors.Default
End Try
End Sub
O mais importante do código da Listagem 7 é novamente o consumo do Web Service. Se o Web Service não retornar nenhum valor no DataSet, sugerimos ao usuário marcar o item como “Dúvida”, caso contrário colocamos os dados de retorno do Web Service que estão no Data Set nos TextBox correspondentes.
Veja na Listagem 8 um exemplo para gravar Item como conferido no banco de dados.
Listagem 8 Gravar dados no banco de dados
Private Sub btn_Confirma_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btn_Confirma.Click
'Verifica se há alguma coisa preenchida na tela
'antes de fazer o update
Cursor.Current = Cursors.WaitCursor
If txt_Ordem.Text = "" Then
Msgbox("Não há nada em Tela para ser Marcado como Conferido!", MsgBoxStyle.Exclamation, "Atenção!")
Cursor.Current = Cursors.Default
Exit Sub
End If
'Rotina para ver se número não foi conferido
If txt_Status.Text <> "NÃO CONFERIDO" Then
Msgbox("Esse número consta como conferido. Você não pode salvar duas vezes!", MsgBoxStyle.Exclamation, "Atenção!")
Cursor.Current = Cursors.Default
Exit Sub
End If
Dim WS As New WebReference.Service1
Dim xSql As String = "sp_PDA_UpdateConferido " & cellValue & "," & txt_Ordem.Text
Try
WS.FazRSsr(xSql)
Msgbox("Conferido!", MsgBoxStyle.Information, "OK!")
LimpaCampos(TabPage2.Controls)
dtg_Conferidos.DataSource = Nothing
dtg_Duvida.DataSource = Nothing
Catch ex As Exception
Msgbox("Erro Confirmar: " & ex.Message, MsgBoxStyle.Critical, "ERRO!")
Finally
Cursor.Current = Cursors.Default
End Try
End Sub
O consumo do Web Service no código da Listagem 8 destina-se a gravar dados no banco de dados. Passei uma Stored Procedure com o código do item conferido para o Web Service, que cuidou de gravar os dados na base SQL Server. Caso o Web Service tenha algum problema para gravar os dados, o tratamento do Catch mostrará um MsgBox com o erro.
Os Tabpages3 e 4 possuem DataGrids para ajudar o usuário a verificar o que foi gravado como conferido e o que foi gravado como dúvida. O código é basicamente consumir o mesmo Web Service para carregar o DataGrid e a formatação usa a mesma Classe de Formatação de Grid utilizada acima.
Quando o item pesquisado não for encontrado na Base de Dados, o usuário deverá digitar os dados do item e gravá-lo na base como “Dúvida”. Para executar esse procedimento, consumi a mesma Sub do Web Service usado para gravar o Item como “Conferido”, passando para isso o nome da Stored Procedure adequada ao Web Service.
Conclusão
Com este projeto, o tempo de execução de conferências de estoque foi reduzido à metade. E, com o complemento do programa na aplicação Desktop, o tempo para solução das dúvidas também foi reduzido à metade, pois com a aplicação migrada e funcionando On-Line os encarregados puderam acompanhar de seus Desktops o andamento da conferência em tempo real.
A partir deste projeto, a equipe ganhou em produtividade e desenvolveu outros aplicativos com sucesso em muito menos tempo do que era feito no eVB. Além disso, não foram mais necessárias correções manuais resultantes de falhas na replicação e sincronização dos dados.
O projeto em .Net economizou muito na codificação, se comparado ao eVB, mas o que mais tornou interessante a migração foi o fato de ela nos permitir utilizar a mesma plataforma de desenvolvimento que a aplicação Desktop. Como as duas aplicações se completam, isso torna o desenvolvimento muito mais produtivo.