Crystal Reports - emitindo cartas , usando fórmulas e tratando datas.


Para acompanhar este artigo você vai precisar ter instalado pelo menos a versão 4.6 do Crystal Reports. Vamos mostrar como emitir cartas para aniversariantes selecionando o aniversariante e o mês desejado. Veremos também como acessar um banco de dados com senha e outras cosas mas... O acesso aos dados será feito usando uma conexão DAO , mas você pode alterar para ADO ou RDO sem problemas...

Se você instalou o Crystal Reports na versão 5.0 do VB não terá que instalá-lo novamente quando migrar para a versão 6.0. Se você não instalou , o Crystal Reports não é instalado automaticamente com a versão 6.0 e você vai precisar instalá-lo separadamente. Para isto inicialize o arquivo de instalação Crystl32.exe presente no diretório \COMMON\TOOLS\VB\CRYSREPT no CD 1 do seu VB 6.0

Vamos usar um banco de dados chamado clientes.mdb e acessar a tabela Clientes de forma a selecionar os aniversariantes de um determinado mês. A base de dados Clientes.mdb possui a senha de acesso igual a 123456.

Criando uma carta personalizada no Crystal Reports.

Podemos começar criando o relatório aniversarios.rpt no Crystal Reports:

1- Inicie o Crystal Reports 

  1. no Menu File selecione a opção New. 
  2. A seguir selecione na janela - Create New Report - o Expert - Standard
  3. Na janela - Create Report Expert - clique sobre o ícone Data file , selecione o banco de dados com o qual deseja trabalhar e a seguir clique no botão Add e depois no botão Done.

2- Na aba Fields selecione os campos da tabela para a qual deseja gera o relatorio. Usaremos apenas o campo Nome.

A estrutura da tabela Clientes é :

- ID

- Nome

- Endereço

- Telefone

- Nascimento

3- A seguir já podemos visualizar o relatório e fazer os ajustes de forma compor uma carta que será emitida para o aniversariante; clique no botão Preview Report e a seguir na aba Design ; esconda(hide) as seções Page Header e Page Footer e faça os ajustes dispondo os campos e mensagens na seção Details conforme indicado abaixo:

1- Aqui usaremos uma formúla que irá usar o campo Nascimento da tabela clientes para compor a data do aniversário no formato : dia / mes de aniversário e ano ano atual : dd/mm/yyyy.

2- Iremos incluir o campo Nome da tabela Clientes para exibir o nome do aniversariante.

3- A mensagem: PARABÉNS

4- A mensagem: Muitas Felicidades e Saúde neste dia Especial

5- A mensagem: É o que lhe deseja

6- A mensagem: Toda a Equipe da

7- A mensagem: aqui você informa o nome da empresa

4- Vamos agora gerar a fórmula que irá exibir o dia e mês do aniversário e o ano atual. Para isso vamos ter que usar o campo Nascimento da tabela Clientes. Iremos extrair deste campo o dia e o mês da data de nascimento e o ano iremos extrair da data atual. Clique na opção do menu Insert e Formula Field, a seguir informe o nome da formula - mes - e use o código conforme figura abaixo:

Vamos explicar a fórmula:

1-ToText( Day({Clientes.Nascimento}),0) - Extraimos o dia de nascimento do campo Nascimento da tabela Clientes e convertemos o resultado em uma string.

2-["Janeiro","Fevereiro","Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro","Dezembro"]
[Month ({Clientes.Nascimento})]

Aqui estamos extraindo um valor de uma matriz pelo indice indicado. Ex: [ 100 , 200 , 300 ] [2] irá extrair o valor 200. No nosso caso o valor retornado por [Month ({Clientes.Nascimento})] será um número que indicará o mês de nascimento do cliente ; logo este número será o índice que usaremos para extrair o mês correspondente da matriz. Ex:

["Janeiro","Fevereiro","Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro","Dezembro"][2] irá retornar -> Fevereiro

3-ToText (Year(today),0) - retornamos o ano da data atual e o convertemos para uma string.

Os sinais de + indicam que estamos concatenando cada parte da string gerada. Após verificar a sintaxe da fórmula coloque-a no lugar apropriado do nosso relatório. Pronto ! podemos salvar o relatório - aniversarios.rpt. ( vamos salvar no diretório c:\teste)

Obs - Voce deve configurar a impressora na opção Selecione Printer Setup do menu File indicando a impressora a ser utilizada, lembrando que na configuração da impressora deve estar selecionado o tamanho do papel a ser utilizado, pois o Crystal faz a leitura do setup da impressora para formatar o relatório.

Criando o projeto no Visual Basic

Bem , agora que já temos o relatório pronto e salvo vamos criar o projeto no Visual Basic que vai usar efetivamente o relatório e permitir ao usuário a seleção das opções.

- Inicie um novo projeto no VB e no formulário padrão - frmcartas - insira os controles e defina os títulos como na figura abaixo:

- Inclua também no seu projeto o módulo de código (.bas) com o nome de cartas.bas.

- Inclua o controle - CrystalReport1 - no seu projeto

- Não esqueça de incluir uma referência ao library DAO - Microsoft DAO 3.6 Object Library - para acesso ao banco de dados.

O que o nosso projeto vai fazer ?

a - Vamos abrir o banco de dados Clientes.mdb. Para complicar um pouco vamos atribuir uma senha ao banco de dados. A senha é 123456. Lembre-se que a senha do banco de dados deve ser informada quando vamos abrir o arquivo. Como não vamos criar um formulário de Login teremos que passar a senha para via código.

Eu vou usar os arquivos de perfis (.INI) e vou armazenar o caminho do banco de dados , a senha do banco de dados e o caminho para a localização do relatório. Ao meu arquivo INI vou dar o nome de CONF.INI . Sua estrutura será a seguinte:

[Geral]
Caminho=C:\teste\clientes.mdb
[Senha]
Senha=365<?:
[Cartas]]
Cartas=c:\teste
Você percebeu que a senha esta codificada?

Pensou que eu ia colocar a senha do banco de dados sem codificar no arquivo INI ?

Vamos ter que descodificar a senha ao passá-la para abrir o arquivo usando a rotina cripsenha.

Nosso arquivo CONF.INI possui três seções : Geral , Senha e Cartas. (Leia o resumo - O que é um arquivo INI?)

b- Após abrir o banco de dados vou carregar o formulário frmcartas

c- Na carga do formulário frmcartas vamos abrir a tabela clientes e preencher as combobox - combo1 e combo2 com os nomes do clientes e com os mês do ano respectivamente.

d- Após isto o usuário deverá selecionar o cliente e o mês para o qual deseja gerar a carta. Se não escolher um cliente iremos gerar a carta para todos os clientes referentes ao mê informado. O mês é escolha obrigatória. Ao término basta clicar no botão Imprimir e o relatório será gerado.

A tabela clientes possui os seguintes dados. (Estou exibindo os dados para você acompanhar)

Vamos agora ao código do projeto:

Na seção General Declarations do módulo vamos declarar as variáveis visíveis em todo o projeto e as declarações API´s para ler os arquivos INI. (Leia o artigo - Show do Zecão - que trata de arquivos INI)

Option Explicit

Public caminho As String
'guarda o caminho do banco de dados
Public senha As String
'guarda a senha do banco de dados
Public cartas As String
'guarda o caminho do relatório crystal reports

Public area As Workspace
Public db As Database

'api para leitura de arquivos de perfil (ini)
Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lpFileName As String) As Long

Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long

A seguir o código da rotina Main(). Essa será a rotina que será executada primeiro quando o projeto for executado :

Sub Main()
On Error GoTo base_erro 'ativamos o tratamento de erros

If Dir(App.Path & "\conf.ini") <> "" Then
  caminho = ReadINI("Geral", "Caminho", App.Path & "\conf.ini")
  senha = ReadINI("Senha", "Senha", App.Path & "\conf.ini")
  cartas = ReadINI("Cartas", "Cartas", App.Path & "\conf.ini")
Else
  MsgBox " O ARQUIVO DE CONFIGURAÇÃO - CONF.INI - NÃO FOI LOCALIZADO !"
  Exit Sub
End If

DBEngine.SystemDB = App.Path & "\system.mdw"
Set area = DBEngine.Workspaces(0)
'esta é a senha do banco de dados , não confunda com a senha do sistema de arquivos
senha = CripSenha(senha)
Set db = area.OpenDatabase(caminho, False, False, ";PWD=" & senha)

frmcartas.Show
Exit Sub

base_erro:
  MsgBox Err.Description, vbCritical, "JcmSoft"
  End 'uma maneira muito bruta para encerrar um aplicação
End Sub

Vamos explicar o código acima:

f Dir(App.Path & "\conf.ini") <> "" Then
  caminho = ReadINI("Geral", "Caminho", App.Path & "\conf.ini")
  senha = ReadINI("Senha", "Senha", App.Path & "\conf.ini")
  cartas = ReadINI("Cartas", "Cartas", App.Path & "\conf.ini")
Else
  MsgBox " O ARQUIVO DE CONFIGURAÇÃO - CONF.INI - NÃO FOI LOCALIZADO !"
  Exit Sub
End If

- Neste código verificamos se o arquivo conf.ini esta no diretório da aplicação(Dir(App.Path & "\conf.ini"))

- Se o arquivo conf.ini existir passamos a leitura dos valores referente as chaves : Geral, Senha e Cartas; usamos para isto a função ReadINI, e , armazenamos os valores em variáveis com escopo Global ; caso contrário informamos ao usuário que o arquivo não existe e saimos da aplicação

- A seguir definimos a localização do arquivo do sistema - DBEngine.SystemDB = App.Path & "\system.mdw"

- Definimos uma área de trabalho padrão para o nosso ambiente - Set area = DBEngine.Workspaces(0)

- Invocamos a função CripSenha para traduzir a senha do banco de dados e armazená-la na variável senha

- Abrimos o banco de dados - Clientes.mdb - usando a localização encontrada no arquivo conf.ini (caminho) com a senha

- finalmente exibimos o formulário da aplicação - frmcartas

Agora vou mostrar o código da função ReadINI presente no arquivo de módulo - cartas.bas. (a função para escrever em um arquivo INI - WriteINI - esta no código , embora não a utilizemos)

Public Function ReadINI(Section As String, Key As String, FileName As String)
'Filename=nome do arquivo ini
'section=O que esta entre []
'key=nome do que se encontra antes do sinal de igual
 Dim retlen As String
 Dim Ret As String
 Ret = String$(255, 0)
 retlen = GetPrivateProfileString(Section, Key, "", Ret, Len(Ret), FileName)
 Ret = Left$(Ret, retlen)
 ReadINI = Ret
End Function

Este arquivo lê o arquivo de configuração conf.ini

A seguir o código da função CripSenha que encripta e descripta uma string. (esta é uma função bem simples , num artigo futuro pretendo abordar a criptografia usando o algoritimo MD5)

Public Function CripSenha(strSenha As String) As String
'A mesma rotina encripta e descripta
Dim nLetra$, cSenha$, L%
strSenha = Trim(strSenha)
For L = 1 To Len(strSenha)
    nLetra = Asc(Mid(strSenha, L, 1))
    nLetra = Chr(Trim(Str(Val(nLetra) Xor (L * 2))))
    cSenha = cSenha & nLetra
Next
CripSenha = cSenha
End Function

Passemos agora para analizar o código presente no formulário - frmcartas . O primeiro código a ser executado é o código presente no evento Load do form. O seu código é o seguinte:

Private Sub Form_Load()
  dim rsclientes as recordset
  Dim vetor As Variant  
  vetor = Array("Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro")
    
  Set rsclientes = db.OpenRecordset("clientes", dbOpenTable)
  rsclientes.Index = "CodigoID"
  
  If rsclientes.RecordCount > 0 Then
    Do While Not rsclientes.EOF
      Combo1.AddItem rsclientes("nome")
      Combo1.ItemData(Combo1.NewIndex) = rsclientes("ID")
      rsclientes.MoveNext
    Loop
    Combo1.ListIndex = 0
  Else
    MsgBox "Não há clientes cadastrados !!!! "
    Exit Sub
  End If
  rsclientes.Close
  
  enche_combo Combo2, 12, vetor
  Combo2.ListIndex = Month(Now) - 1
    
End Sub

Vejamos o significado deste código:

- Primeiro declaramos a variável objeto rsclientes do tipo Recordset e um vetor do tipo Variant:

Dim rsclientes as recordset
Dim vetor As Variant

- A seguir iniciamos o vetor com os meses do ano :

vetor = Array("Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro")

- Abrimos a tabela clientes e ativamos o índice - CodigoID - para o recordset rsclientes

Set rsclientes = db.OpenRecordset("clientes", dbOpenTable)
rsclientes.Index = "CodigoID"

- Verificamos se há registros no recordset e em caso positivo percorremos o recordset - rsclientes - preenchendo o controle combobox - combo1. Estamos usando a a propriedade ItemData da ComboBox. Esta propriedade permite associar um número inteiro a cada item de um ComboBox ou ListBox. Ela é um vetor de valores inteiros com o mesmo número de itens da Combo ou do ListBox , desta forma a combo exibe o nome dos clientes mas quando você selecionar um cliente estaremos usando o seu campo ID para realizar as operações.

If rsclientes.RecordCount > 0 Then
   Do While Not rsclientes.EOF
     Combo1.AddItem rsclientes("nome")
     Combo1.ItemData(Combo1.NewIndex) = rsclientes("ID")
     rsclientes.MoveNext
   Loop
   Combo1.ListIndex = 0
 Else
   MsgBox "Não há clientes cadastrados !!!! "
   Exit Sub
 End If

- Fechamos o recordset clientes - rsclientes.Close

- Preenchemos o controle combobox - combo2 - invocando a função enche_combo ; esta função recebe como parâmetros o nome do controle : Combo2 , o total de itens : 12 e o vetor com o nomes a serem atribuidos. O código da função enche_combo é dado a seguir:

Public Sub enche_combo(cbo As Control, tamanho, vetor)
Dim i As Integer

For i = 0 To tamanho - 1
  cbo.AddItem vetor(i)
  cbo.ItemData(cbo.NewIndex) = i
Next

End Sub

Para encerrar falta somente o código do evento click do botão de comando - command1. Este código irá criar o formulário de acordo com a seleção do usuário. Vejamos o seu código:

Private Sub Command1_Click()
Dim criterio As String
Dim periodo As String

CrystalReport1.DataFiles(0) = caminho

'relatorio de aniversarios do mes
CrystalReport1.SelectionFormula = ""
CrystalReport1.Destination = 0 'janela
CrystalReport1.ReportFileName = "aniversarios.rpt"

If Combo2.ListIndex <> -1 Then
  If Combo1.ListIndex <> -1 Then
    CrystalReport1.SelectionFormula = "{clientes.nome}='" & Trim(Combo1.Text) & "'"
  Else
    CrystalReport1.SelectionFormula = "Month({clientes.nascimento})=" & Combo2.ItemData(Combo2.ListIndex) + 1
  End If
Else
   MsgBox "Selecione um mês !!! "
Exit Sub
End If

CrystalReport1.Connect = "DSN=;UID=;PWD=" & senha
CrystalReport1.password = senha
CrystalReport1.Action = 1

End Sub

Vejamos o significado do código acima:

- CrystalReport1.DataFiles(0) = caminho - define a localização do banco de dados e tabelas usadas para construir o relatório. (Se você mudar o seu banco de dados de lugar e não definir a localização do Crystal vai 'reclamar')

- No código a seguir temos :

1-CrystalReport1.SelectionFormula = ""
2-CrystalReport1.Destination = 0
'janela
3-CrystalReport1.ReportFileName = "aniversarios.rpt"
1- Definimos a propriedade SelectionFormula como vazia

2- Determinamos que o relatório será exibido na tela

3- Informamos o nome do relatório que vamos usar

- A seguir verificamos se no controle combobox - combo2 - temos um mês selecionado , se não houver seleção informamos ao usuário a mensgem - Selecione um mês !! ; se houver , então verificamos a seguir se no controle combobox - combo1 - temos um nome selecionado; se o usuário não selecionar um nome montamos a linha atribuindo a propriedade SelectionFormula todos os clientes com data de nascimento no mê selecionado:

- CrystalReport1.SelectionFormula = "Month({clientes.nascimento})=" & Combo2.ItemData(Combo2.ListIndex) + 1

Se o usuário informar um nome atribuimos propriedade SelectionFormula o nome do cliente indicado no combobox - combo1 :

- CrystalReport1.SelectionFormula = "{clientes.nome}='" & Trim(Combo1.Text) & "'"

- A seguir atribuimos a propriedade Connect o DSN , o nome do usuário e a senha. Como nosso banco de dados possui somente a senha , indicamos apenas este valor. (Este parâmetro somente é requerido quando aplicado ao driver ODBC que estivermos usando. No caso estamos usando o driver ODBC para o Access.)

- Usamos a propriedade password para atribuir a senha usada no banco de dados Access. A sintaxe é :

[form.]Report.Password[= Password$]

CrystalReport1.Connect = "DSN=;UID=;PWD=" & senha
CrystalReport1.password = senha

- Para encerrar usamos a propriedade Action para disparar a execução do relatório - CrystalReport1.Action = 1

Agora acabei...Inté o próximo artigo...


José Carlos Macoratti