Assim se programava a impressão de um relatório no século passado

A organização que fiz nos meus backups antigos segue rendendo bons assuntos! Após eu descobrir os scripts em Shell que utilizava no meu saudoso servidor Debian, agora descobri algo ainda mais antigo: os sistemas em Clipper que fiz nos anos 1990 e início dos 2000! É com muita satisfação que compartilho com vocês trechos de um deles, mais precisamente de uma função que gera um dos relatórios do sistema, além de também mostrar qual era o meu livro de cabeceira na época.

O inesquecível livro "do barquinho"


O grande José Ramalho


Talvez alguns de vocês estejam se perguntando: sistemas em Clipper em pleno anos 2000? Sim! Nesta época eu utilizava além do Clipper o Visual Basic para sistemas que requeressem uma GUI (“for Windows”), porém quando fazia as primeiras visitas ao cliente para produzir a análise verificava que não raro a melhor configuração disponível era um 80486 ou mesmo um 80386. Imagine qual performance teria um software feito em Visual Basic ou outra linguagem “visual” qualquer nestes equipamentos? E quase sempre com restrições orçamentárias no projeto (muito devido ao pão-durismo de boa parte dos contratantes), não havia como dizer ao cliente algo como “para rodar este sistema de forma decente você vai precisar no mínimo de um Pentium II” – o Pentium II era o topo de linha na época entre os processadores x86 e um PC baseado nele custava uma pequena fábula.

Já sistemas feitos em Clipper, desde que tomando certos cuidados na sua programação, voavam baixo mesmo em um 386 ou 486 e atendiam plenamente ao que se propunham, então mesmo no início dos anos 2000 produzir softwares em Clipper era muito mais do que costume, mas sim quase uma premissa do projeto pelas restrições de hardware.

O livro é recheado de funções e dicas de como utilizá-las


Mas vamos ao que interessa: se hoje em dia basta “mandar tudo pro PDF”, antigamente codificar um relatório era algo um tanto quanto mais trabalhoso. Confiram aqui partes do código comentado de uma função de relatório de um dos meus sistemas, um gerenciador de frota de veículos.


*******************************************************
** Sistema de Controle da Frota de Veiculos
** Cliente XXXXXXXXXXX         
** Michael Rigo                     
** 05/08/2000 - 02:36
** Funcao de Resumo de Abastecimentos                    
*******************************************************

#Include "SETCURS.CH"

#Include "INKEY.CH"

********************

Function Impresger()
********************
Public cPrefveiger:=Space(5),;
       cModeveiger:=Space(40),;
       cPlacveger:=Space(8),;
       cMarcveiger:=Space(15),;
       cAreaveger:=Space(6),;
       cCombger:=Space(10),;
       nAnoger:=0,;
       nKmatger:=0,;
       nKmantger:=0,;
       dDatatger:=Ctod(""),;
       dDatanger:=Ctod(""),;
       nKmrodger:=0,;
       nValoveiger:=0,;
       nGerpag:=1,;
       nGerlin:=1,;
       nAuxnfger:=0,;
       dAuxDatger:=Ctod(""),;
       nAuxlitger:=0,;
       nAuxvabger:=0,;
       nContabager:=0,;
       nAuxvalmanger:=0,;
       nContmanger:=0,;
       nContaciger:=0



Inicialmente chamamos duas bibliotecas do Clipper: a SETCURS.CH permite manipular o formato do cursor na tela e a INKEY.CH permite ler o buffer do teclado para controlar quais teclas foram pressionadas. Em seguida vem a declaração de variáveis: as declaradas com Space são caracteres, as com Ctod são datas e as demais são numéricas. Como curiosidade, note no cabeçalho a data e a hora em que comecei a trabalhar neste relatório - o dia 5 de agosto de 2000 foi um sábado, realmente eu não tinha vida social nesta época. :p


Do While .T.

   Tone(400,2)
   SetColor("15/4")
   @ 13,23 Say "Onde deseja imprimir o resumo?"
   @ 15,22 Prompt "Impressora"
   @ 15,33 Say (Chr(174)) + (Chr(175))
   @ 15,36 Prompt "Arquivo TXT"
   @ 15,44 Say (Chr(174)) + (Chr(175))
   @ 15,47 Prompt "Cancelar"
   Menu To Menuger
   SetColor("15/1")



Aqui monto um menu para o utilizador selecionar a saída do relatório, se na impressora ou em um arquivo texto. O comando Tone emite um aviso sonoro pelo alto falante (buzzer) do sistema.


   Do Case

      Case Menuger = 1
           SetColor("15/4")
           @ 15,26 Say "Imprimindo resumo..."
           Inkey(0.2)
           SetColor("15/1")

           If .Not. IsPrinter()

              Alert("A impressora nao esta pronta... Verifique se esta ligada e se os cabos estao conectados corretamente")
              Restore Screen From TelaIni
              Exit
           Endif

           Set Device To Printer

           Set Printer To LPT1

      Case Menuger = 2

           Set Device To Printer
           Set Printer To RESUMOGE.TXT
         
      Case Menuger = 3 .Or. Lastkey() = K_ESC
           Restore Screen From TelaIni
           Exit     
      Endcase


Caso a saída for na impressora o sistema testa se a mesma está pronta para imprimir, se não estiver interrompe o processo com uma mensagem de alerta. Caso esteja tudo certo a impressão é direcionada para a porta paralela (LPT1) – dispositivos USB ainda eram bem raros na época. Se a saída for em um arquivo texto é feito um direcionamento para o arquivo em si.


       Abrevei()

    Vei->(DbGotop())
    AbreIab()
    Itaba->(DbGoTop())
    Abreaba()
    Abas->(DbGoTop())
    Abreman()
    Man->(DbGotop())
    Abreaci()
    Aci->(DbGotop())
    Imprge()
Enddo
DbCloseAll()
Restore Screen From TelaIni
Exit


É feita a abertura das tabelas de onde buscaremos os dados do relatório e chamamos a função que efetivamente o gera (Imprge). Após o final do processamento fechamos todas as tabelas abertas com a função interna do Clipper DbCloseAll – isto é necessário pois bases de dados em dBase tem um problema crônico de corrupção dos seus índices caso as mesmas não sejam fechadas corretamente, por exemplo, caso o equipamento trave ou ocorra uma falta de energia elétrica.


******************

Function Imprge()
******************
Do While .T.

   Recuperager()

   Gercab()
   @ 8,3 Say "Dados do veiculo"
   @ 9,1 Say Replic ("-",76)
   @ 10,3 Say "Prefixo................: " + cPrefveiger
   @ 11,3 Say "Placa..................: " + cPlacveger
   @ 12,3 Say "Area...................: " + cAreaveger
   @ 13,3 Say "Marca..................: " + cMarcveiger
   @ 14,3 Say "Combustivel............: " + cCombger
   @ 15,3 Say "Ano de fabricacao......: " + Str(nAnoger)
   @ 16,3 Say "Kilmetragem atual......: " + Str(nKmatger)
   @ 17,3 Say "Kilometragem anterior..: " + Str(nKmantger)
   @ 18,3 Say "Kilometros rodados.....:" + Str(nKmrodger)
   @ 20,3 Say "Dados dos Abastecimentos"
   @ 21,1 Say Replic ("-",76)
   nGerlin:=22


Aqui começamos efetivamente o relatório de abastecimentos dos veículos com o seu cabeçalho.


   Do While .Not. Itaba->(Eof())

      If Itaba->Pveiaba = cPrefveiger
         If dDatanger <= Itaba->Dataaba .And. Itaba->Dataaba <= dDatatger
            @ nGerlin,3 Say "Nota fiscal...............: " + Str(Itaba->Numnfite)
            nAuxnfger:=Itaba->Numnfite
            nGerlin++
            Testafiger()
            @ nGerlin,3 Say "Data......................: " +  Dtoc(Itaba->Dataaba)
            dAuxDatger:=Itaba->Dataaba
            nGerlin++
            Testafiger()
            @ nGerlin,3 Say "Litros abastecidos........: " + Str(Itaba->Litraba)
            nAuxlitger = nAuxlitger + Itaba->Litraba
            nGerlin++
            Testafiger()
            @ nGerlin,3 Say "Valor do abastecimento....: " + Str(Itaba->Valolit)
            nGerlin++
            Testafiger()
            @ nGerlin,3 Say "Servicos efetuados........: " + (Itaba->Servaba)
            nGerlin++
            Testafiger()
            @ nGerlin,3 Say "Valor total...............: " + Str(Itaba->Valoaba)
            nAuxvabGer = nAuxvabGer + Itaba->Valoaba
            nGerlin++
            Testafiger()

            Do While .Not. Abas->(Eof())

               If Abas->Numnf = nAuxnfger .And. Abas->Databas = dAuxDatger
                  @ nGerlin,3 Say "Posto.....................: " + Abas->Nopoaba
                  nGerlin++
                  Testafiger()
                  @ nGerlin,3 Say "Funcionario...............: " + Abas->Nofuaba
                  Exit
                Else
                   Abas->(DbSkip())
                   Loop
                Endif
            Enddo

            Itaba->(DbSkip())

            nGerlin = nGerlin + 2
            Testafiger()
            nContabager++
            Loop

          Else

            Itaba->(DbSkip())
            Loop
            Endif
       Else
          Itaba->(DbSkip())
          Loop
      Endif
   Enddo

   Testafiger()



Agora buscamos e imprimimos os dados de abastecimentos de todos os veículos da frota um a um, em um laço de repetição que rodará até que todos os registros do BD de abastecimentos e veículos tenham sido lidos até o final (Eof) - a função DbSkip pula uma linha no banco de dados. Note que a cada linha impressa é necessário controlar o número de linhas do relatório (a variável nGerlin e a função Testafiger) para que a página não acabe e a impressora continue a imprimir “no vazio”.


   If nContabager = 0

      @ nGerlin,3 Say "Nao houveram abastecimentos no periodo"
   Else
      @ nGerlin,3 Say "Litros abastecidos........: " + Str(nAuxlitger)
      nGerlin++
      Testafiger()
      @ nGerlin,3 Say "Numero de abastecimentos..: " + Str(nContabager)
      nGerlin++
      Testafiger()
      @ nGerlin,3 Say "Kilometros rodados........: " + Str(nKmrodger)
      nGerlin++
      Testafiger()
      @ nGerlin,3 Say "Media Kilometro/litro.....: " + Str(nKmrodger / nAuxlitger)
      nGerlin++
      Testafiger()
      @ nGerlin,3 Say "Valor total gasto.........: " + Str(nAuxvabger)
   Endif

  Vei->(DbSkip())


   If Vei->(Eof())

      Eject
      Set Device To Screen
      Exit
   Endif

   Zerager()

   Itaba->(DbGotop())
   Eject
   Loop

Enddo



Aqui faço a totalização dos gastos com abastecimento do veículo e pulo para o próximo veículo do BD, enquanto a tabela de veículos não chegar ao fim (Vei->Eof).


*********************
Function Recuperager()
*********************
cPrefveiger:=Vei->Prefvei
cModeveiger:=Vei->Modevei
cPlacveger:=Vei->Placvei
cMarcveiger:=Vei->Marcvei
cAreaveger:=Vei->Areavei
cCombger:=Vei->Combvei
nAnoger:=Vei->Anovei
nKmatger:=Vei->Kmatvei
dDatatger:=Vei->Datkmat
nKmantger:=Vei->Kmanvei
dDatanger:=Vei->Datkman
cPorteger:=Vei->Portvei
nKmrodger:=nKmatger - nKmantger



Esta função carrega as variáveis com os dados do BD dos veículos.


*****************

Function Gercab()
*****************
@ 2,1 Say Replic ("-",76)
@ 3,3 Say "Resumo do veiculo..: " + cModeveiger 
@ 4,3 Say "Emitido em: " + Dtoc(dDataSis) + " - " + Time() + "         Pg. " + Str(nGerPag)
@ 5,3 Say "Periodo de " + Dtoc(dDatanger) + " ate " + Dtoc(dDatatger)
@ 6,1 Say Replic ("-",76)



Função que gera o cabeçalho do relatório.


*********************

Function Testafiger()
*********************
If nGerlin > 60
   Eject
   nGerlin:=8
   nGerpag++
   Gercab()
Endif



Função importantíssima, pois testa se chegamos ao final da página baseado no número de linhas já impressas (a variável nGerlin). Caso já tenhamos chegado ao final da página (60 linhas impressas), comandamos a impressora para quebrar a página (Eject), geramos um novo cabeçalho na nova página a partir da linha 8 e finalmente incrementamos a variável que conta o número de páginas (nGerpag).


******************

Function Zerager()
******************
cPrefveiger:=Space(5)
cModeveiger:=Space(40)
cPlacveger:=Space(8)
cMarcveiger:=Space(15)
cAreaveger:=Space(6)
cCombger:=Space(10)
nAnoger:=0
nKmatger:=0
nKmantger:=0
dDatatger:=Ctod("")
dDatanger:=Ctod("")
nKmrodger:=0
nValoveiger:=0
nGerpag:=1
nGerlin:=1
nAuxnfger:=0
dAuxDatger:=Ctod("")
nAuxlitger:=0
nAuxvabger:=0
nContabager:=0
nAuxvalmanger:=0
nContmanger:=0
nContaciger:=0



Sempre que necessário as variáveis são zeradas, como para efetuar os cálculos de um novo veículo.

Convém também lembrar que alguns comandos de controle da impressora, tais como o Eject, eram padronizados. Porém caso você precisasse de um relatório com efeitos diferenciados era necessário recorrer ao data sheet da impressora, bem como readequar a função de impressão em caso de substituição da mesma. Como bem sabemos, o MS-DOS não funciona como o Windows e as próprias aplicações devem incluir drivers para os dispositivos utilizados.

Enfim, esta é a forma deliciosa que produzíamos um relatório em uma linguagem clássica! Espero que os mais velhos tenham gostado de relembrar e que os mais novos tenham gostado de conhecer os primórdios das tecnologias atuais. Em tempo, gostaria também de agradecer ao amigo Jeferson Mombach de Sousa pela sugestão do tema. Um forte abraço a todos e até a próxima!

Comentários

  1. Sugestão: código fica bem melhor de ler com uma fonte monoespaçada.

    ResponderExcluir

Postar um comentário