quarta-feira, 30 de dezembro de 2009

Manipulando Imagens

Essa é bem simples, mas ajuda, a questão do tratamento de imagens em Genexus X, que a partir desta versão passou a tratar também do gerenciamento das imagens do projeto.

Genexus guarda as imagens do projeto em Customization->Images, sendo que todas devem ser importadas pela ferramenta com a opção New Image. Duplo clique sobre uma imagem nesta pasta permitirá abri-la para edição, e um novo duplo clique sobre a imagem aberta permitirá trocá-la por outra.

As imagens são armazenadas por default na pasta web/Resources do projeto, e caso a KB tenha definida vários idiomas e na carga da imagem seja especificado este tratamento, então uma imagem para cada idioma do projeto será colocada nas pastas web/Resources/.

Para incluir a imagem em uma interface utilize o controle Image na paleta, até aqui muito simples.

Acesso Runtime

Aqui a coisa fica mais interessante, ou seja, como manipular ou acessar a imagem na referida pasta.

Usando um controle TextBlock é possível apresentar a imagem, desde que este seja definido como Format=HTML. Daí é só programar a tag HTML , no formato a seguir.



(clique na imagem para aumentar)

Observe que o nome da imagem (nomeimagem) e a chamada a Link() retornam o endereço relativo (a partir da pasta raiz do sistema ‘web’), permitindo que a mesma seja acessada pelo browser. Em outras palavras, a chamada devolve algo como Resources/nomeimagem.gif

Botão com Imagem


Usando este simples recuso é possível programar um botão diferente com texto e imagem.



Este pode ser construído a partir de uma variável (Varchar(300), Readonly=true e Format=HTML) ou mesmo em um TextBlock.






Em ambos os casos é possível interceptar o pressionamento com um simples

Event &BotaoPrint.Click
...
EndEvent

Bitmap

Outra maneira de manipular uma imagem é através de variável Bitmap, sendo que neste caso, é necessário carregar a imagem a partir da função loadbitmap(). O mesmo resultado obtido por Link() pode ser utilizado para carregar a imagem na variável bitmap.




Antes de Fechar a Conta
A imagem que se encontra na pasta Resources ou nas pastas de idiomas, podem ser manipuladas (desde que não se troque o nome das mesmas) normalmente. Ou seja, para a realização de uma eventual manutenção, não é necessário recarregá-la no Genexus, bastando a gravação da modificação no arquivo de imagem nas referidas pastas.
Conclusão
Pequenas coisas que auxiliam na construção de interfaces interessantes e muitas outras podem ser criadas com esses recursos simples, deixemos isto para outra ocasião.

sábado, 5 de dezembro de 2009

Genexus é lento?

Muitas pessoas, ao ouvirem a respeito de Genexus, argumentam que a ferramenta é lenta no acesso ao banco de dados, e portanto, não é interessante. Meu argumento sempre foi o de que por facilitar a manutenção, a ferramenta poderia até ser um pouco mais lenta no acesso à base, mas que a longo prazoo sistema teria menor custo.

Bom, me deparei com uma situação que de fato trazia grande desconforto na montagem de uma determinada interface, porque levava segundos até ser apresentada para o usuário, em um sistema Web, e o pior era que essa interface tinha que ser reconstruída a cada clique do usuário na tela.

Isso me levou a descer um pouco mais nas profundezas do gerador, e tentar entender o porque do acesso á base deixava a interface tão lenta. No final das contas o acesso que tinha que fazer era a uma tabela com alguns joins inner e outer, mas que no final retornava em torno de 150 registros. Ou seja, nada muito complexo a ponto de justificar segundos para a montagem da interface.

Primeiramente descobri que o gerador inclui as queries SQL no próprio código gerado em C#, portanto podem ser estudadas e até mesmo testadas externamente, basta um CTRL + C e CTRL + V, no SQL Server, substituindo evidentemente os parâmetros da query, isso pode ser interessante, porque no final das contas se fosse programado manualmente a query gerada não seria muito diferente da que o Genexus cria. Portanto, alguma coisa não estava muito evidente, porque se a query era a mesma gerada manualmente, porque a interface estava lenta.

Eis um exemplo da query do Genexus no código:

new GeneXus.Data.NTier.ADO.CursorDef("T00012", "SELECT [CustomerId], [CustomerName], [CustomerAddress], [CustomerGender] FROM [Customer] WITH (UPDLOCK) WHERE [CustomerId] = @CustomerId ",true, GxErrorMask.GX_NOMASK, false, this,prmT00012,1,0,true,false )

Em seguida reprogramei o acesso a base de dados, desta vez programando a query manualmente na própria ferramenta, ou seja, em código nativo C#. Esperava que com isso o resultado fosse melhorar significativamente, mesmo porque deu muito trabalho para programar na mão o que o Genexus fazia apenas colocando um Grid e os atributos, e o resultado infelizmente não ajudou muito, continuava lento.

Enfim, não era o acesso que estava complicando, no meu caso, e sim a montagem da interface gráfica. Observando atentamente o que havia feito cheguei a conclusão que a interface possuía:

1. Um grid Freestyle com muitas variáveis (nenhum atributo) porque a programação do acesso foi totalmente manual
2. Muito código na área de eventos, e muitos repetidos, porque era necessário muitos cálculos e navegação nos dados recuperados da base.
3. Armazenamento de informações na própria interface gráfica (na prática estava colocando uma SDT do tipo collection na interface para recuperação dos flags gerados pelo usuário)

Recomendação

Após muita programação e reprogramação da interface, cheguei a algumas conclusões interessantes, e que acho que podem te ajudar em problemas semelhantes.

Em primeiro lugar deixe a database para o Genexus mesmo, não vai melhorar muita coisa, talvez alguns milisegundos, mas o argumento da manutenção continua a ser o mais importante, porque uma coisa é resolver o problema para entregar o programa e outra é resolver problemas após a entrega, que no meu ponto de vista é pior.

Outra coisa, fique atento ao uso de variáveis na interface, pois são mais lentas que atributos, evitando colocar muitas no Grid. Cheguei à conclusão que é melhor uma única com todos os valores em uma string do que várias com os valores separadinhos na interface. Mesmo que isso signifique que se tenha que separa os valores (SPLIT) para as utilizar.

Não siga a lógica do armazenamento dos valores na interface para uso na carga POST da interface (aquela realizada por uma ação do usuário), mesmo porque o Genexus vai executar tudo novamente, o START, a carga do grid, a leitura da interface, portanto, é mais fácil armazenar na base o que o usuário realizou e em seguida recalcular tudo do que guardar na interface o que o usuário fez.

A Interface

Após falar tanto, acho que voce deve ter ficado um pouco curioso a respeito da tal interface gráfica complexa, então vou deixar uma imagem aqui para que voce durma esta noite.

Não cabe aqui discutir muito a respeito da operação (matricula de alunos), mas trata-se de uma interface baseada em clicks nas caixinhas, onde a cor azul = selecionado, amarelo = escolhido pelo usuário e vermelho = bloqueado devido à escolha. A cada confirmação o usuário escolhe o recurso e a interface deve ser reconstruída.
Esta interface roda na Web, atualmente, quase com o mesmo desempenho de uma interface Windows local.
Conclusão

Talvez esse artigo sirva para os programadores mais experientes, e que tenham grandes desafios em interfaces complexas, e não para os iniciantes, mas posso dizer com certeza que Genexus é uma ferramenta poderosa no acesso á base e não deixa nada a desejar à programação tradicional e manual. Se dominada, permitirá a você fazer grandes coisas com pouco esforço.

sexta-feira, 6 de novembro de 2009

Runtime Error

Já passou por isso? localmente a aplicação roda sem nenhum problema, mas no servidor surgem erros estranhos.

Durante uma semana me debrucei sobre um erro de execução na aplicação que era causado quando se abria uma transação. Alguns paineis de pesquisa abriam normalmente, portanto a primeira coisa que veio a mente foi que a database, por alguma razão, tinha incompatibilidades com o protótipo.

Depois de muitos backups, restores, reconstrução das tabelas manualmente, cópia dos registros, o erro permanecia.

Me lembrei de um detalhe, certos erros aparecem localmente quando acessados, e ao ingressar no Terminal Server, entendi o que estava acontecendo. Posso te dizer uma coisa, nunca imaginaria que a causa era essa.




Traduzi a aplicação para português, reconstruindo todos os objetos, publiquei e... tudo certo!

Caso passe por um problema similar, vai ai uma dica: rode a aplicação no servidor em modo localhost, provavelmente o erro correto será revelado.

sábado, 22 de agosto de 2009

Arquivos Excel no Genexus

Genexus possui recursos muito simples e interessantes que implementam um 'que' de aplicação integrada, e que podem ser utilizados em seus projetos sem maiores complicações. Um desses recursos é a geração de arquivos XLS (MS-Excel), e que opera de forma similar ao de geração de DOC´s (MS-Word). Nesta página daremos o caminho das pedras para utilizar esse recurso, com a contribuição, do Eng. Claudio Shinji, autor original do trabalho.

Antes de mais nada é necessário obter e instalar uma biblioteca chamada JAKARTA-POI, através do site http://sourceforge.net/projects/jbyjsharp/files/Jakarta%20POI/, e que originalmente consiste na API 'Java' para manipulação de arquivos no formato OLE2 Microsoft, mas que no projeto do sourceforge foi gentilmente traduzido para .NET e J#. Esse exemplo foi programado no Genexus 9.0, X e Ev RC, e .NET C# sem maiores complicações, se seu ambiente não for esse, favor providenciar as bibliotecas adequadas.

OLE2 Microsoft é um formato OpenXML, que na prática permite a leitura e escrita de arquivos MS Word, MS PowerPoint e MS Excel. A instalação deve ser feita dentro da pasta BIN que se encontra na pasta WEB do modelo criado no Genexus, entendendo-se 'instalação' apenas a cópia das DLL´s para esse local.

Para evitar maiores confusões também instale o Microsoft J#, que pode ser obtido em http://www.microsoft.com/downloads/details.aspx?familyid=F72C74B3-ED0E-4AF8-AE63-2F0E42501BE1&displaylang=enhttp://www.microsoft.com/downloads/details.aspx?familyid=F72C74B3-ED0E-4AF8-AE63-2F0E42501BE1&displaylang=en

A planilha será criada em um local abaixo do diretório ROOT da aplicação, ou seja, em nosso caso, abaixo da pasta WEB. Voce pode inclusive criar uma pasta chamada EXCEL para guardar esses arquivos organizadamente. O acesso ao arquivo gerado deve ser feito pela web por meio de uma URL que aponte para esse arquivo nessa tal pasta EXCEL. Desta forma são necessárias duas informações: local fisico de armazenamento (C:\Inetpub\wwwroot\AplicacaoGenexus\ Excel\planilha.xls) e a URL que aponta para o arquivo (http://www.algumlugar.com.br/%20aplicacaogenexus/excel/planilha.xls)

Programação da Coisa

Passada a fase dos ajustes no servidor, agora voce deve programar (enfim!), e não muita coisa é necessária para se produzir a planilha.

Declaração de variáveis, apenas uma Planilha do tipo ExcelDocument, uma linha do tipo Numeric e uma Arquivo do tipo Character(30).

A programação é simples, bastando seguir os passos apresentados a seguir.

Event 'excel'
&Planilha.UseAutomation = 0
&Arquivo = 'C:\inetpub\wwwroot\Aplicacao\excel\planilha.xls'
&Planilha.Open(&Arquivo)

if &Planilha.ErrCode <> 0
msg(&Planilha.ErrDescription)
else
&Planilha.Clear()

&Planilha.Cells(1,1).Text = 'Id'
&Planilha.Cells(1,1).Italic = 1
&Planilha.Cells(1,1).Bold = 1

&Planilha.Cells(1,2).Text = 'Nome'
&Planilha.Cells(1,2).Italic = 1
&Planilha.Cells(1,2).Bold = 1
&linha = 2

For each line in Grid1
&Planilha.Cells(&linha,1).text = CidadeId.ToString()
&Planilha.Cells(&linha,1).Color = 3
&Planilha.Cells(&linha,2).Text = CidadeNome.ToString()
&Planilha.Cells(&linha,2).Color = 3
&linha = &linha + 1
endfor

&Planilha.Save()
TextBlock1.Caption = "planilha"

endif

EndEvent

Em vermelho voce encontra um controle TextBlock (HTML format) que deve apontar para a URL onde foi gravada a planilha. Em azul o local fisico onde o arquivo da planilha deve ser criada.


O exemplo abre um GRID em um WebPanel apresentando uma tabela de Cidades, e em seguida através de um botão, o evento 'Excel' é chamado e a planilha criada com os registros apresentados no GRID. Observe que o tratamento da planilha é realizado por meio de suas células, e a programação apenas preenche essas células com informação.
E o resultado é uma planilha que pode ser visualizada clicando-se no link 'planilha'.
Bom trabalho!



terça-feira, 7 de julho de 2009

Datetime

Manipular tipos de dados Datetime é simples, ao contrário do que parece, basta utilizar as funções corretas.

Genexus possibilita a definição de atributos do tipo Date e DateTime, e em ambos os casos, no momento da criação na tabela, pelo menos no SQL Server, o que se cria na verdade é um DateTime.

O ambiente proporciona ainda duas variáveis &Time (caracter) e &Today (date), que retornam a hora e data atual respectivamente, e que facilitam a vida na hora da programação. As funções de manipulação de Data e Hora em Genexus normalmente operam em modo texto, também com o intuito de facilitar a vida.

Datetime

No caso de Datetime temos a função NOW() que retorna a combinação de Data+Hora, e que pode ser armazenada em um atributo Datetime, inclusive se este for uma fórmula definida na estrutura.

Já para o caso de operações de soma ou subtração de períodos Datetime, temos duas funções:

  • &result = TAdd(&date1, &sec) Retorna em &result a soma entre a data &date1 mais a quantidade de segundos informado em &sec.
  • &result = TDiff(&date1, &date2) Retorna em &result a diferença (em segundos) das datas &date1 e &date2

Caso seu interesse seja horas, divida o resultado de TDiff por 3600, mas não se esqueça de declarar &result com casas decimais, como Numeric(5.1), por exemplo.

Mais simples que isso, só as constantes data e datetime utilizando #, para se criar datas conhecidas e diferentes da atual, mas isso é assunto para outra publicação.

sexta-feira, 3 de julho de 2009

Web Service

Começei a estudar isso a um bom tempo, 2005 para ser mais preciso, lembro que um aluno meu, Fábio, inclusive escreveu sobre isso em uma monografia excelente, a qual fui orientador. E por causa disso mesmo, não me arrisquei a utilizar esse recurso. Muito bom, excepcional mas de difícil programação. Muitas definições, muitos acrônimos (SOAP, WSDL,..., XML),... vamos falar a verdade, isso assusta mesmo.

Acontece que quando precisamos não tem desculpas para não tentar, necessidade não pede licença. E foi isso que aconteceu, tive que programar o tal Web Service. E lendo o material da Artech sobre isso comecei a me assustar, porque novamente toda aquela teoria voltava, mas enfim vamos mandar ver.

Comecei programando uma Procedure, simples de inicio, que apenas devolvia uma mensagem a quem a chamasse e a fui incrementando de forma que essa mesma procedure inseria, ao final, um registro em uma tabela.



Programei as propriedades da Procedure, como indicado pela Artech:

Main program: true
Call Protocol: SOAP

Observe que na Procedure, que chamei de TicketWebServAdd, foi programada uma variável do tipo SDT, declarada como &ticket, que tinha alguns dos campos que seriam carregados remotamente, tipo TicketData, TicketTitulo, TicketComentario, coisas assim..., Essa SDT foi criada a partir de uma tabela Ticket, portanto, refletia a própria estrutura da tabela.

Em seguida, por recomendação da Artech, seria necessário executar a chamada a essa Procedure, mais com uma pequena alteração, colocando-se um 'a' antes do nome da Procedure, e também passando como parâmetro wsdl. A URL resultante foi: http://localhost/Academica.TicketSystem.NetEnvironment/aticketwebservadd.aspx?wsdl

O IE respondeu com um belo XML cheio de informações, que bom parece que funcionou! Pela minha experiência no assunto, me parecia que a Procedure estava na Web pronta para responder às chamadas.


Tratei da outra ponta então, do programa consumidor, em outra KB. Para isso, primeiramente executei nessa nova KB a ferramenta WSDL Import (Tools->Application Integration->WSDL Import). Informando apenas a mesma URL anterior, e pressionando NEXT.


E em seguida IMPORT.


Genexus criou dois objetos na KB consumidora, um External Object, chamado de TicketWebServAdd, e uma SDT TicketWebServAddTicket_SDT. Que interessante, a ferramenta WSDL Import trouxe a definição da SDT que havia programado na outra KB para dentro desta nova.

Dai foi só colocar num painel as variáveis de entrada e programar a chamada do Web Service, que ficou mais ou menos assim:


E no botão Confirmar, a chamada ao evento 'ENTER' que carregava a SDT de informação e a passava para o External Object.



Só isso? não se esqueça que a variável &ticket foi declarada como uma TicketWebServAddTicket_SDT e a &ticketws como TicketWebServAdd. E ainda que na chamada, a variável &ticket está sendo passada, portanto na Procedure (da outra KB) deve haver uma regra Parm programada. A minha foi programada assim:



O resultado! ao rodar o Painel (Consumidor na KB cliente), o registro era inserido na tabela Ticket pela Procedure (Web Service na KB remota).



Bom, estou até envergonhado, quando comecei a escrever esse artigo pensei em fazer algo diferente do que a própria Artech já havia feito e escrito, mas no final das contas ficou igual :(. Falta então explicar o que é um Web Service, assim fica pelo menos diferente na ordem das coisas, e vou tentar fazer isso de forma bem simples em um único parágrafo.

"Web Service é um recurso de integração entre aplicações distintas, com diferentes linguagens, plataformas, banco de dados, por meio de chamadas através da Internet. Em outras palavras, um programa que conecta aplicações distintas na Web. "

Já pensou em quantas coisas legais dá pra fazer com isso! até ligar sistemas legados à Web.

É isso, palmas para Artech que conseguiu transformar um Dragão em uma Cinderela.

sexta-feira, 19 de junho de 2009

Coisas que a natureza não consegue explicar

Quatro dias brigando com o Windows 2003 Server, IIS, FTP, privilégios de usuários, ASP.NET ou IUSR_..., e pra que serve o IWAN_..., roteiro de filme de terror!

Pois é, as vezes os computadores são excelentes para ganharmos tempo, tornar a nossa vida mais ágil, melhorar nossa comunicação com o mundo, enfim, tudo que se ouve por ai. Para um desenvolvedor web, no entanto, essa história é inversa. Computadores nos deixam doidos, estressados, perdemos muito tempo na frente deles, e quase sempre por uma razão estúpida.

A história é a seguinte, de repente nossas atualizações no servidor não eram mais aceitas pelo IIS, as páginas eram apresentadas com erros, informando que certos arquivos javascript não eram existentes na aplicação (o pior é que os arquivos estavam lá). Problemas de FTP, pois os arquivos Zipados não podiam ser abertos, e o servidor respondia com um sonoro 'Access is denied' para esses arquivos. Por outro lado, quando zipavamos alguns arquivos no desktop, os mesmos eram aceitos e abertos no servidor normalmente. Parecia até intriga da novela das 8h.

Nada na internet a respeito, nenhum forum debatendo sobre bloqueio de arquivos ZIP no servidor IIS, nenhuma situação semelhante ocorrida na Artech, pois para Genexus basta copiar todos os arquivos e a aplicação .Net está pronta para rodar. E isso é verdade mesmo, muito simples.

Enfim, várias noites de insonia, sistema em vias de entrar em produção, nada resolvido, um comportamento muito estranho do servidor, e de repente ... aparece a Carol. Nada como ouvir a Carol dizer, deixa comigo!

E sabe o que ela descobriu? O desfecho da novela, o gran-finale, ...

O anti-virus não gostava de arquivos javascript e bloqueava tudo, sem perdão, sem perguntar, sem informar nada. Simplesmente, aqui não!!! ,#$!!@#

Se um dia entrar em uma novela dessas, não tenha dúvidas, é o anti-virus.

Final feliz!

quinta-feira, 18 de junho de 2009

Surpresa... Genexus é Server!

Toda a comunidade Genexus ansiava por um mecanismo de compartilhamento da base de conhecimento entre desenvolvedores, que permitisse que todos tivessem acesso às mais novas mudanças no projeto, e claro, evitando retrabalho entre outras chatices.

É bom lembrar que o software livre trouxe como beneficio exatamente isso, a distribuição de tarefas entre programadores espalhados em todo mundo, graças a sistemas de controle como o CVS (Control Version Systems), amplamente utilizado em ambientes Linux.

Genexus deu um passo largo nesse sentido, pois além de permitir o compartilhamento da KB, controle de publicação, histórico de modificações de objetos, controle de releases e versões, possibilita ainda rodar Genexus no browser.

Consegue visualizar isso! você em um local sem seu notebook, mas necessitando modificar algum objeto de seu sistema, entrando em um Cyber Café, e programando Genexus no Internet Explorer ou Chrome? O melhor disso é que isso já funciona, e está à sua disposição.

Ficou curioso? entre no site http://gxserver.genexusx.com/genexusserver/, instale antes o Genexus X Evolution 1 Beta 2, e poderá abrir o Genexus Web e também baixar as kb´s que se encontram nesse servidor.



Qualquer dia desses escrevo um pouco mais de CVS, commit, rollback,... Tema essencial para gerenciamento de projetos de software.

quarta-feira, 17 de junho de 2009

f (x) => Transação=Z(Atributos)

Esse é o titulo mais sugestivo que eu achei para dizer que Transação = Soma(Atributos), pena que a fonte não ajudou.

Um ponto fundamental de Genexus é o nome escolhido para designar um atributo.

Eramos livres, e de certa forma irresponsáveis, quanto a essa questão e gostavamos de designar nomes bonitos e práticos como CódigoDoCliente, EndereçoDoFornecedor,... E muitas vezes para controlar os registros colocávamos flags, do tipo, RegistroApagado, DataCriação, em todas as nossas tabelas. Coisa chique, e ainda dava um ar de programa bem feito, bem documentado, bem construido, organizado às nossas aplicações.

Com Genexus começamos a tratar os atributos de forma mais responsável, pois este elemento ganha uma responsabilidade maior, pois através do nome do atributo Genexus nos diz se o "conceito" é o mesmo ou não.

Por exemplo, ClienteId é um conceito que representa o código do cliente em toda a aplicação. Cliente é para nós um agrupamento de conceitos (informações, regras, ...), ou um objeto. Em Genexus buscamos organizar os conceitos no objeto colocando o nome do objeto seguido do conceito, assim obtém-se uma referência lógica ou organização.

A regra nos diz que:

* Conceitos Iguais = Nomes Iguais
* Nomes Diferentes = Conceitos Diferentes

Um conceito somente poderá aparecer em uma tabela no sistema todo, a menos que seja um campo chave. Por exemplo: ClienteNome (NomeDoCliente) somente aparecerá em uma tabela no sistema, enquanto que ClienteId (CódigoDoCliente), que é campo chave poderá aparecer em várias tabelas, e quando isso ocorrer haverá relação entre esta tabela com a tabela de Clientes.



Assim começa a mágica da 3a. Forma Normal em ação...

segunda-feira, 15 de junho de 2009

Iniciando do inicio...

Vamos começar com Genexus...

Transações em Genexus são os objetos fundamentais para a geração da infra-estrutura da aplicação. Apesar da interface gráfica obtida, de um ou dois níveis com grids,... o essencial deste objeto, no entanto, é o que não aparece.

Através das transações o Genexus aciona a sua "maquininha" de gerar tabelas na terceira forma normal. Se não sabe o que é terceira forma normal, então é melhor iniciar seus estudos e pesquisas antes de se aventurar a criar uma aplicação séria. Muitos desenvolvedores "sérios" nem sabem o que é isso, e o resultado é a necessidade de se programar muito para se fazer pouca coisa.

Genexus te dá a vantagem de produzir um modelo de base de dados neste formato, sem que voce tenha que se preocupar. "Corrigindo", na verdade, voce terá que se preocupar sim, porque suas definições levam a um bom ou mal modelo, e não queremos iniciar nosso projeto com um mal modelo, não é mesmo.

Então estaremos dedicando esse espaço para discutirmos um pouco do que vem a ser esse bom modelo de base de dados em aplicações Genexus.

Boas vindas!

Olá Pessoal,

Depois de muito resistir a um blog, pois tenho claro que para se aventurar nessas novas ondas são necessários alguns requisitos, dos quais o principal eu não possuo. Tempo é um luxo que já deixei de contar a muito "tempo".

Mas como diria os bons administradores, tudo é uma questão de "administração", portanto, cometerei esse pequeno pecado na minha agenda, e buscarei, com certa mas imprecisa regularidade, expor minhas experiências na administração de equipes e desenvolvimento de software, principalmente Genexus.

Engraçado, levei menos de cinco minutos para colocar esse Blog no ar, acho que isso é um bom sinal!

Seja benvindo e obrigado por sua compreensão.

Um abraço
Douglas