sexta-feira, 23 de dezembro de 2011

Importar produtos de outros sistemas para o Protheus

Pessoal, desenvolvi um programa em C# que faz a importação de produtos de qualquer base de dados para o Protheus. Fica aí mais um exemplo de integração entre Protheus e C#.

Este vídeo fala do passo a passo dos procedimentos iniciais





Este outro vídeo mostra o programa funcionando



Aqui o link para baixar o programa
www.4shared.com/rar/qDP_ytZM/Imp_SB1.html?

quarta-feira, 16 de novembro de 2011

Navegador Web feito em Advpl

Abaixo estou disponibilizando um código que monta um navegador em advpl. Ele guarda as páginas visitadas e permite a navegação entre elas como acontece em um navegador web.

Obs: Para funcionar, é preciso configurar o smartcliente.ini e colocar logo abaixo da marcação [config] a opção:
BrowserEnabled=1




#include "protheus.ch"


User Function Navegador()

Private aPages := {} // array que guarda os endereços visitados
Private nPgVist := -1 // controle a posicao do array aPages

Private aSize := MsAdvSize() // pega o tamanho da tela
Private oDlg1, oTIBrw
Private cNavegado := Space(80) // usado no objeto get para guardar os endereços web
Private lcont := .T.

DEFINE MSDIALOG oDlg1 TITLE "Navegador" From aSize[7],0 to aSize[6],aSize[5] of oMainWnd PIXEL

cNavegado := "http://www.google.com" // pagina inicial
oNav:= TGet():New(10,10,{|u| if(PCount()>0,cNavegado:=u,cNavegado)}, oDlg1,340,5,,,,,,,,.T.,,,,,,,,,,)

@ 010, 350 Button oBtnIr PROMPT "Ir" Size 40,10 Action(Processa({||Navegar()},"Abrindo","Aguarde...")) Of oDlg1 Pixel
@ 010, 390 Button oBtnImp PROMPT "Imprimir" Size 40,10 Action oTIBrw:Print() Of oDlg1 Pixel



@ 010, 430 Button oBtnAnte PROMPT "Anterior" Size 40,10 Action (Retorna()) Of oDlg1 Pixel
@ 010, 470 Button oBtnDep PROMPT "Avançar" Size 40,10 Action(Avanca()) Of oDlg1 Pixel
@ 010, 510 Button oBtnSair PROMPT "Sair" Size 40,10 Action(Sair()) Of oDlg1 Pixel



oTIBrw:= TIBrowser():New( 025,010,aSize[5]-640, 270, "http://www.google.com", oDlg1 )

aaDD(aPages,"http://www.google.com")

oNav:bLostFocus := { || Valido()}


Activate MsDialog oDlg1 Centered

Return


Static Function Navegar()

Ir()

Return


Static Function Ir()

oTIBrw:Navigate(AllTrim(cNavegado),oDlg1)
aaDD(aPages,AllTrim(cNavegado))
nPgVist := Len(aPages)

Return



Static Function Avanca() // proxima proxima pagina que jah foi visitada

if(Len(aPages) > nPgVist .and. Len(aPages) > 1 )
nPgVist++
oTIBrw:Navigate(aPages[nPgVist],oDlg1)
cNavegado := aPages[nPgVist]
oNav:Refresh()
EndIf

Return


Static Function Retorna() // pagina anterior que foi visitada
if(nPgVist>1)
nPgVist--
oTIBrw:Navigate(aPages[nPgVist],oDlg1)
cNavegado := aPages[nPgVist]
oNav:Refresh()
EndIf

Return



Static Function Sair() // fecha a tela
oDlg1:End()
Return



Abaixo um print da tela:


quarta-feira, 5 de outubro de 2011

Relatórios Mensais

Algumas vezes eu preciso gerar relatórios que mostrem dados entre determinados meses. Abaixo, um trecho desse tipo de relatório.


Basicamente, o usuário informa o mês e ano inicial, mês e ano final e a partir daí eu gero um array contendo o intervalo. Ele tem a estrutura conforme figura abaixo:


Posso então usar os elementos deste array como parâmetros em consultas que gerem tabelas temporárias além de imprimi-los no relatório conforme a primeira figura.
Abaixo, as funções que geram o array.


Agora a função auxiliar da função acima que retorna o nome do mês:

quarta-feira, 10 de agosto de 2011

Integração do Protheus com C# e .Net


Hoje irei mostrar um outro exemplo de integração do Protheus com DotNet usando a linguagem C#. Neste exemplo irei solicitar ao protheus o cálculo do valor de parcelas (simulação de parcelas), ou seja,irei fazer a conexão da minha aplicação em DotNet com o servidor de aplicações, passarei alguns parâmetros como valor total, código da condição de pagamento e irei obter um array como retorno informando a data de vencimento e valor da parcela. Pra começar é necessário registrar a ocx apconnxcontrol.ocx e fazer referência da mesma no Visual Studio, para isso crie um novo projeto do tipo Console e siga os passos mostrados da publicação anterior "Integração do Protheus com C# ". Feito isso, minimize o Visual Studio, crie e compile o seguinte código fonte no Protheus.


No fonte eu expliquei que poderia referenciar diretamente a função do Protheus que realiza as simulações, mas tive dificuldade em passar como parâmetro o tipo data, então pra não ficar defendendo teoria, criei a user function que faz o tratamento da data que é passada pelo Visual Studio como string.

Voltando ao Visual Studio, criei uma classe que realiza a conexão com o Protheus e também um método que aciona minha user function e obtém o resultado da simulaçao de parcelas. Observe o código da classe e seus comentários.



E na classe principal irei criar o seguinte código:


Para ver o programa funcionando, veja o vídeo abaixo:
obs: Veja em tela cheia para melhor visualização.











segunda-feira, 8 de agosto de 2011

integração do Protheus com C#


Nessa postagem darei um exemplo bem simples de como integrar o Protheus com Csharp. Primeiramente temos que registrar no windows um arquivo ocx que está dentro do smartclient chamado apconnxcontrol.ocx . Para registrar é simples, eu copiei o arquivo para a pasta System32 do windows, logo depois executei o cmd.exe e fiz o seguinte comando:
regsvr32 apconnxcontrol.ocx . Lembrando que no Windows 7 você tem que executar o cmd.exe como administrador, caso contrário não dará certo.









Concluída essa etapa, é hora de abrir o Visual Studio.
No exemplo a seguir eu usei o VS 2010 e criei um novo projeto do tipo Console. Faça o mesmo e com o Program.cs aberto clique em adicionar uma nova referencia conforme figura abaixo:


Usando a guia Browse vá até a pasta System32 do Windows e selecione o arquivo apconnxcontrol.ocx:


Nessa etapa já temos o "link" com nossa ocx do protheus. Agora é só por a mão na massa e começar a brincar com as funções. No exemplo abaixo criei uma User Function no protheus que retorna um array de string. Após compilado, fui no Visual Studio e feito as devidas referências eu fiz com que o código em C# executasse a minha User Function do protheus e exibisse o retorno dela no Console.

//user function compilada no protheus
#INCLUDE "rwmake.ch"

User Function RetTest()

Local aTeste := {}

aaDD(aTeste,"teste1")
aaDD(aTeste,"teste2")
aaDD(aTeste,"teste3")

Return aTeste

Código no C#, observe as referências :






Se tudo der certo teremos o resultado abaixo:



Tem uma documentação de RPC nos fóruns de advpl, lá tem mais detalhes. Espero ter colaborado com algum conhecimento e lembrando que podemos usar a ocx em outras linguagens que tem suporte a esse tipo de arquivo. Até a próxima.










quarta-feira, 13 de abril de 2011

Conectar o Protheus a outra Base de Dados 2

Hoje irei detalhar melhor e passo a passo como podemos fazer uma integração entre o cadastro de clientes de um outro sistema e o sistema Protheus. A idéia é a seguinte:
Ter um JOB que perceba uma inclusão, alteração ou exclusão de um cliente no outro sistema e reflita isso para o Protheus. Nosso primeiro obstáculo é o banco de dados, pois nessa demonstração o sistema Protheus funciona com SQL Server e o sistema que será integrado usa o PostGress. Apesar disso, podemos conectar o Protheus ao banco do outro sistema e extrair as informações do cliente e tratá-las para inserir no cadastro. Vamos então realizar as configurações para que o Protheus tenha acesso a base de dados PostGre.

1)Baixe e instale o Drive ODBC no site http://www.postgresql.org/ftp/odbc/versions/msi/
2) Após instalar, crie a conexão ODBC no mesmo servidor aonde está configurada a ODBC do Protheus para o BD PostGre e teste a conexão.

3)Com a conexão OK é hora de criar o TOPConnect para a ODBC criada.

4)Valide a conexão.

alt=""id="BLOGGER_PHOTO_ID_5595066254285455986" />
















5) Para testar, podemos criar um pequeno Rdmake que irá na tabela do Postgre e trazer o nome do primeiro cliente cadastrado. Para visualizar melhor,abra a figura em uma nova guia.





Na próxima publicação eu irei mostrar como construir uma arquitetura de comunicação entre o Protheus e PostGre e assim deixar pronta a integração de clientes.

segunda-feira, 11 de abril de 2011

DbTree recursiva 2


Na última postagem falei sobre a DbTree baseada em uma única tabela mas que tem infinitas hierarquias. Hoje irei postar a estrutura da tabela e o código principal para poder gerar a árvore.
Primeiramente temos a estrutura da tabela, a qual chamei de ZRA. Eu a fiz para funcionar no Protheus mas ela pode ser usada para qualquer linguagem:

ZRA_COD -> Caracter tamanho 6. Irá guarda o ID único do registro.(Tem que ser auto-incremento. No caso do Protheus eu uso o GetSXEnum("ZRA","ZRA_COD") )

ZRA_DESCRI -> Caracter tamnho 20. Terá a descrição do registro.

ZRA_PAI -> Caracter tamanho 6. Irá guarda o ID único do nó pai. É extremamente importante que este campo tenha as mesmas características do campo ZRA_COD.

ZRA_ATIVO -> Caracter tamanho 1. Criei esse campo como um tipo de "flag" que terá a letra "S" se o nó estiver ativo e "N" caso não. Possibilita desativar um nó sem ter que exclui-lo.

ZRA_BMP1 -> Caracter de tamanho 10. Guarda o nome do ícone que o nó terá.

ZRA_BMP2 -> Caracter de tamanho 10. Guarda o nome do ícone que o nó terá quando estiver aberto, ou seja, exibindo os nós filhos.


ZRA_NIVEL -> Numérico. No caso do Protheus, esse campo será preenchido com 1 caso seja um nó raiz ou 2 caso seja um nó filho de outro nó.

A figura tenta ilustrar genericamente o algoritmo da função principal e abaixo o código em advpl.

Static Function LoadTree(lAt)
Local cATivo := iif(lAt,"S / N","S")
Local aGetArea := GetArea()



if(Empty(ZRA->ZRA_PAI))
Return
EndIf
oTree1:BeginUpdate()
if(AllTrim(ZRA->ZRA_PAI) == "000000" .and. Alltrim(ZRA->ZRA_ATIVO) $ cAtivo )

if(oTree1:Total()>0 .and. nContNo > 0)
oTree1:EndTree()
EndIf

if(! oTree1:TreeSeek(ZRA->ZRA_COD))
oTree1:AddTree(ZRA->ZRA_DESCRI + iif(Alltrim(ZRA->ZRA_ATIVO)=="N"," [DESATIVADO]","")+Space(24),.T.,"FOLDER5","FOLDER6",,,ZRA->ZRA_COD)
EndIf

Elseif(Alltrim(ZRA->ZRA_ATIVO) $ cAtivo)
if(! oTree1:TreeSeek(ZRA->ZRA_COD))
if oTree1:TreeSeek(ZRA->ZRA_PAI)
oTree1:AddItem(ZRA->ZRA_DESCRI + iif(Alltrim(ZRA->ZRA_ATIVO)=="N"," [DESATIVADO]","")+Space(24),ZRA->ZRA_COD,iif(Alltrim(ZRA->ZRA_ATIVO)=="N","F5_VERM","GEOTRECHO"),,,,2)
EndIf
EndIf

EndIf


nContNo++
ZRA->(DbSkip())
if(ZRA->(EOF()))
nContNo := 0
oTree1:EndTree()
RestArea(aGetArea)
oTree1:EndUpdate()
Return
EndIf
LoadTree(lShowDe)

Return

quinta-feira, 7 de abril de 2011

DbTree recursiva



O objeto Dbtree tem usos bem interessantes. Um dos que eu acho mais legal é a construção de uma estrutura DbTree baseada em uma única tabela e que pode conter infinitas hierarquias. O melhor é que a lógica pode ser usada não só para o componente usando na programação Advpl mas em outras linguagens. Já criei a mesma rotina em Visual Basic 6.0 como também no Access. A cima, um print da tela e em seguida outro print com as funções dos objetos contidos nela. Também coloquei um vídeo mostrando a rotina em funcionamento.
Na próxima publicação irei mostrar a estrutura da tabela e o algorítimo que realiza todas as funções.



segunda-feira, 14 de março de 2011

Erros na Rotina Automática

Alguns erros que ocorrem nas rotinas automáticas podem ser exibidos através do comando MostraErro(). Porém alguns erros não são detectados e a rotina roda como se estivesse tudo certo, ou seja, como se a gravação tivesse ocorrido com sucesso. Mas quando vamos procurar o registro, ele não se encontra. Passei várias vezes por isso, principalmente em cadastro que tem uma série de validações como por exemplo o cadastro de clientes. O primeiro problema foi quando eu estava fazendo a integração do Protheus com outro sistema e o CPF vinha em branco ou errado. O controle de erro da rotina automática não era acionado e eu não sabia o motivo pelo qual o cliente não estava sendo inserido até que resolvi olhar o CPF e pronto... primeiro problema resolvido. Verifiquei se o sistema mandava todos os campos obrigatórios para o Protheus e criei um log que avisava carro algum estivesse em branco. Para isso usei a função X3Obrigat(cCampo).
Só que não parou por aí pois alguns clientes continuavam a chegar e não eram gravados. Investiguei e descobri que o número do IBGE estava vindo '0000' em muitos.. ou seja.... o sistema que mandava as informações deixava muito a desejar em algumas validações. Mais uma vez isso foi corrigido. Funcionou por um bom tempo até que em alguns clientes de pessoa Jurídica o erro de não gravar continuava. De novo, investigando, descobri que o sistema mandava de forma incorreta a inscrição estadual, ou seja, não vazia a validação antes de mandar para o Protheus e em alguns casos a inscrição vinha nula ou com números digitados de forma aleatória. Depois de corrigido, até o momento a integração de clientes funciona muito bem.

sexta-feira, 11 de março de 2011

Conectar o Protheus a outra Base de Dados






Uma integração entre o Protheus e outros sistemas pode ser feita de várias formas, xml, arquivo txt etc. Eu resolvi fazer algo mais direto, ou seja, uma aplicação no Protheus(BD Sql Server) que enxerga o banco de dados do outro sistema, que usa o BD Postgres, e monta uma tabela temporária tratando esses dados e logo depois inserindo-os no Protheus através de rotinas automáticas. Apesar do banco do outro sistema não ter nada haver com o banco de dados do Protheus, podemos conectar a ele da seguinte forma:
1- Crie uma ODBC no servidor do BD do Protheus para o outro banco. No caso do Postgres, eu tive que baixar um drive para conexão ODBC específica para ele. Observe configuração de porta, senha e tal. No final, verifique se a conexão foi bem sucedida. Caso tenha sido, deveremos executar o próximo passo.
2- Valide a conexão ODBC da mesma forma que se faz com uma ODBC criada para o Protheus, ou seja, vá ao TOTVSDBMonitor e na aba configurações, escolha a aba Postgres ou a do BD do outro sistema que você quer se conectar. Crie um ambiente, coloque a senha correta e no assistente é só fazer a validação da conexão.
3- Com a validação ok, é hora de enxergar o outro banco. Vamos então criar uma rotina básica de comunicação com nosso BD da outra aplicação.

-----------------------------------------------------------------------------------------------
User Function JOBATAE1()
Private cBDProd := "MSSQL/Interface" // comunicacao com BD Protheus. Usado na funcao //TCLINK.
Private cBDAtae := "POSTGRES/atae" // comunicacao com BD sistema ATAE. Usado na //funcao.
Private cIPBD := "192.168.1.244" // IP do servidor de aplicacoes

Private _nTcConn // conectar com ATAE
Private _nTcCon2 // conectar com Protheus


TCCONTYPE("TCPIP") // tipo de conexao que sera usada

_nTcConn := TCLink(cBDAtae,cIPBD) // conexao com ATAE(sistema que usa o BD PostGres)
_nTcCon2 := TCLink(cBDProd,cIPBD) // conexao com Protheus

-----------------------------------------------------------------------------------------------

Neste primeiro trecho observe que criei duas variáves :
cBDProd
cBDAtae

Elas guardam a string de conexao com os respectivos BD. Logo depois temos a variável
cIPBD que contém o IP do servidor de aplicações do Protheus. Essas variáveis serão usadas na função TCLink pois ela tem a seguinte sintaxe:

TCLink(cConectString,cServer)

cConectString: String composta conforme a regra:

Para TOPConnect NT : Nome do Banco de Dados / Nome do Ambiente

Para TOPConnect/400: Nome do Ambiente

cServer: Endereço IP do servidor TOPConnect.

As variáveis _nTcConn e _nTcCon2 recebem o resultado da conexão, ou seja, o TCLink retorna um número negativo caso a conexão não tenho tido êxito. Neste caso existe a possibilidade de algum dos servidores de BD estarem offline ou a string de conexão está incorreta.

Continuando nossa aplicação temos:

-----------------------------------------------------------------------------------------------

If ( _nTcConn <>
//mostra alerta de erro com a conexão BD postgres ou BD Protheus

Alert("*** Erro de conexão ***")
Else
Alert("*** Conexão OK ***")
TCSETCONN(_nTcConn) // setando conexao com Postgres
LerTab() // lendo uma tabela Postgres
InsProt() // pegando os dados do Postgres e inserindo no Protheus
EndIf

Return

Static Function LerTab()
Local cQuery := " SELECT * FROM CLIENTE "
dbUseArea(.T., "TOPCONN", TCGenQry(,,cQuery), "TRS", .F., .T.)
TRS->(DbGoTop())
Alert(TRS->NOME)
Return



Static Function InsProt()
TCSETCONN(_nTcCon2) // conectando com o Protheus
RPCSetType(3)
RPCSetEnv("01","01","user","senhaUser") // logando no Protheus
// faca um loop para carregar os dados da tabela TRS para seu array
// e execute sua rotina automática
TRS->(DbCloseArea())
Return
-----------------------------------------------------------------------------------------------
Na segunda parte da rotina podemos ver que após verificar se as conexões estão Ok, usamos o comando TCSETCONN(_nTcConn) para setar a conexão da rotina para o BD postgres. A partir daí é tudo quase igual a qualquer rotina para criar uma tabela temporária baseada em informações do Protheus. Observe apenas a sintaxe usada na variável cQuery que terá que obedecer as regras do banco. Depois de recuperar as informações da tabela CLIENTE do postgres, é só usar o comando TCSETCONN(_nTcCon2) para se comunicar com o Protheus e a parti daí usar os comandos RPCSetType e RPCSetEnv para logar na empresa e filial. Depois disso, execute sua rotina automática e pronto, sua integração foi feita.



sexta-feira, 4 de março de 2011

Rotinas Automáticas

Uma ferramenta importante para a integração são as rotinas automáticas. No projeto em que eu estou, toda a integração é baseada em rotinas automáticas, salvo algumas exceções em que o Protheus não disponibiliza tais rotinas como uma para gerar uma nota fiscal de saída pelo faturamento. Tive algumas dores de cabeça com algumas rotinas automáticas pois elas são bem rígidas e não deixam passar em branco alguns erros. Inclusive, alguns desses erros são bem difíceis de identificar . Irei listar abaixo alguns deles e suas soluções:


eu estava gerando Notas Fiscais de entrada através da rotina MSExecAuto({|x,y,z| mata103(x,y,z)},aCabec,aItens,3)

Conferi os dados, tudo certinho. Só que na hora de gerar a nota, a função MostraERRO() gerava o seguinte aviso:

HELP: REGNOIS
Não existe registro relacionado a este
código.

Tabela SF1 18/02/11 16:48:56
- cTipo :=N
- cFormul :=N
- cNFiscal :=102454
- cSerie :=01
- dDEmissao :=18/02/11
- cA100For :=000005
- cLoja :=01 < -- Invalido - cEspecie :=NFE - cUfOrig :=PB

Só que a loja era realmente do fornecedor e todos os outros dados estavam corretos. A não ser por um detalhe. O código do fornecedor está com tamanho igual a 6, porém o campo correspondente do Protheus estava com tamanho 14. Nada muito haver com o erro apresentado pelo Protheus, mas que causou bastante dor de cabeça. Então na linha:

aadd(aCabec,{"F1_FORNECE","000005"})

Eu fiz assim:
aadd(aCabec,{"F1_FORNECE","000005 "})
E a rotina funcionou normalmente.
Resumo da história, é altamente recomendável usar códigos com o mesmo tamanho do campo.


quarta-feira, 2 de março de 2011

Integração

Hoje, muitas empresas tem a necessidade de migrar para sistemas mais robustos e confiáveis. O problema é que essa migração não pode ser feita de uma hora para outra. Na verdade para a migração ocorrer, muitas vezes é necessário que os dois sistemas funcionem em paralelo e assim integrem informações até que naturalmente ocorra a total substituição do sistema antigo pelo mais moderno. Outra situação é quando a empresa quer trabalhar com dois sistemas em paralelo, por exemplo, um sistema específico para front de loja e outro para ficar na retaguarda. No meu caso estou fazendo justamente isso. Estou integrando um sistema usado no front de loja e caixa com o sistema Protheus que fica na retaguarda. A grande dificuldade é justamente tratar as diferente regras e situações mas nada impossível de se fazer. Nas próximas publicações irei mostrar toda a arquitetura usada como também como foi feita a primeira parte da integração aonde clientes cadastrados no sistema front de loja vão automaticamente ser cadastrados no Protheus.
Em meu outro Blogger (http://sap-b1-sdk.blogspot.com) estarei falando do sistema SAP B1 e irei mostrar além de exemplos de customizações, integrações com o sistema Protheus na parte de compras, vendas e cadastros. Até breve.