10. Gerando Relatórios
Talvez uma das primeiras aplicações do COBOL no ambiente de negócios tenha sido a geração de relatórios a partir de informações armazenadas em cartões perfurados, fitas e/ou discos magnéticos. A cultura da “informação no papel” vem mudando significativamente nos últimos anos, mas certamente você ainda encontrará milhares de programas na sua empresa que geram relatórios da maneira tradicional.
Hoje boa parte dos relatórios do ambiente corporativo se transformou em XMLs, CSVs e outros formatos similares que podem ser importados para o desktop de trabalho do usuário e tratados nos aplicativos de sua preferência.
A forma como esses formatos são gerados pelo COBOL não é muito diferente da maneira como geramos relatórios tradicionais, aqueles que têm cabeçalho, linhas de detalhe, sumários e rodapés…
Neste capítulo veremos como construir um programa simples para geração de relatórios a partir de arquivos sequenciais em disco. Na verdade, o que faremos é um programa que lê o arquivo de duplicatas válidas que geramos no programa CRP0207 e emite um relatório de contas a receber.
Especificando o relatório
O layout do arquivo de entrada para nosso relatório é o mesmo que usamos para o arquivo de saída do programa CRP0207, e que podemos rever abaixo:
*----------------------------------------------------------------* * DUPLICATAS VALIDAS *----------------------------------------------------------------* FD CRA0207V. 01 CRA0207V-REGISTRO. 03 CRA0207V-NR-FATURA PIC 9(006). 03 CRA0207V-NR-DUPLICATA PIC 9(002). 03 CRA0207V-CD-CLIENTE PIC X(006). 03 CRA0207V-DT-EMISSAO PIC 9(008). 03 CRA0207V-DT-VENCIMENTO PIC 9(008). 03 CRA0207V-VL-FATURA PIC S9(013)V9(002). 03 CRA0207V-CD-CATEGORIA PIC X(003). 03 CRA0207V-ST-DUPLICATA PIC X(003).
Nosso primeiro relatório mostrará uma lista simples dessas duplicatas, sem nenhuma ordenação especial. Uma linha com o número de duplicatas listadas e o somatório dos valores da fatura aparecerá num rodapé que vamos imprimir no final.
A figura abaixo mostra o layout do relatório que queremos gerar:
Pelo layout podemos ter uma ideia de alguns recursos novos que veremos no nosso programa: obtenção de data e hora do sistema, criação de máscaras para exibição de datas e valores monetários, alinhamento de colunas etc.
A grande maioria dos relatórios possui uma ou mais linhas de cabeçalho, que aparecem no início de cada página e normalmente seguem um padrão adotado pela empresa. Repare que as três primeiras linhas do nosso relatório mostram o nome da empresa, nome do sistema, nome do relatório (CRL0208), título do relatório, data e hora de emissão e número de página. Logo depois dessas linhas vemos os nomes das colunas. Essa linha também faz parte do cabeçalho.
Depois do cabeçalho temos as linhas de detalhe, que receberão as informações que serão lidas no arquivo de entrada. Todas as informações que aparecem no layout já existem no arquivo de entrada.
No final da última página do relatório teremos um rodapé onde mostraremos a quantidade de duplicatas impressas e o somatório dos valores de todas as duplicatas listadas.
Tradicionalmente os relatórios eram gerados com 80 ou 132 caracteres por linha, e entre 60 e 66 linhas por página. Isso correspondia aos principais modelos de formulários contínuos disponíveis nas empresas e à fonte monoespaçada que existia nas impressoras matriciais. Hoje essas limitações praticamente não existem e as impressoras possuem milhares de recursos para comprimir e ajustar as fontes para o tamanho de papel desejado. Mas o formato tradicional ainda é o mais usado em sistemas legados.
Iniciando a construção do programa
No COBOL um relatório nada mais é que um arquivo sequencial, onde cada linha de cabeçalho, detalhe ou rodapé é um registro. Os comandos para abertura e fechamento de arquivo e gravação de registros são praticamente os mesmos que usamos nos programas anteriores: OPEN, WRITE e CLOSE, sendo que o WRITE tem algumas opções adicionais.
Muitos anos atrás era comum que o programa enviasse o relatório diretamente para a impressora. Hoje, porém, os relatórios são primeiramente salvos em disco (em arquivos texto) que depois são direcionados por servidores de impressão e/ou outros aplicativos similares para o dispositivo físico (impressora e papel).
Se todo relatório é um arquivo, então precisamos declará-lo na ENVIRONMENT. O trecho abaixo mostra o programa da IDENTIFICATION DIVISION (padrão) até a INPUT-OUTPUT SECTION, onde declaramos tanto o arquivo de entrada quanto o relatório.
*================================================================* IDENTIFICATION DIVISION. *----------------------------------------------------------------* PROGRAM-ID. CRP0208. AUTHOR. PAULO ANDRE DIAS. DATE-WRITTEN. 07/01/2017. REMARKS. *----------------------------------------------------------------* * SISTEMA: CR - CONTAS A RECEBER * JOB: 02 - GERACAO DE FLUXO DE CAIXA * PROGRAMA: 08 - LISTA DUPLICATAS VALIDAS * * OBJETIVO: LER O ARQUIVO DE DUPLICATAS VALIDAS E GERAR UMA * LISTA SIMPLES PARA CONFERENCIA * * VERSOES: DATA DESCRICAO * ------ --------------------------------------- * XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *----------------------------------------------------------------* *================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * DUPLICATAS VALIDAS COPY CRS0207V. * LISTA DE DUPLICATAS PARA CONFERENCIA SELECT CRL0208 ASSIGN TO "$DAT/CRL0208.TXT" ORGANIZATION IS LINE SEQUENTIAL.
A declaração do relatório, neste caso, não usa nenhum book; o comando SELECT é codificado diretamente no programa e o mesmo acontecerá com o detalhamento do arquivo, mais a frente, na FILE SECTION.
Repare que a cláusula ASSIGN TO está associando o nome interno CRL0208 a um nome externo “$DAT/CRL0208.TXT”. Isso significa que o programa vai gerar o relatório como um arquivo texto em disco. Depois de gerado, poderemos direcioná-lo para qualquer servidor ou aplicativo de impressão se quisermos efetivamente vê-lo no papel.
O relatório é organizado como LINE SEQUENTIAL (como os arquivos anteriores) porque queremos que cada linha termine com um line feed (no Linux) ou carriage return + line feed (no Windows). Isso fará com que o arquivo possa ser carregado num editor de texto simples.
Nos programas anteriores, quando detalhamos nossos arquivos sequenciais na FILE SECTION, tivemos que declarar todos os campos que faziam parte dos registros daqueles arquivos. Quando trabalhamos com relatórios, porém, o detalhamento do arquivo indica apenas um tamanho fixo para as linhas do relatório (80 ou 132) e isso tem um motivo.
Relatórios possuem cabeçalhos, rodapés, linhas de separação e até linhas de detalhes com muitos valores fixos. Nosso layout, por exemplo, tem nome da empresa, nome de sistema e alguns labels como “Data”, “Hora” e “Pagina”. É muito mais prático criar esses labels como constantes na WORKING-STORAGE (usando a cláusula VALUE) do que preenchê-los com o comando MOVE cada vez que uma linha tiver que ser gravada no relatório. Esse conceito ficará mais claro ao longo do programa. Por ora vamos apenas “detalhar” nosso relatório informando que cada uma de suas linhas terá o tamanho fixo de 80 caracteres:
*================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * DUPLICATAS VALIDAS COPY CRF0207V. * LISTA DE DUPLICATAS PARA CONFERENCIA FD CRL0208. 01 CRL0208-REGISTRO PIC X(080).
Como todo arquivo, o nome que indicamos na FD precisa ser igual ao nome que usamos na ENVIRONMENT: no caso, CRL0208. Usamos também a mesma convenção de usar o nome do arquivo como prefixo para o nome do registro, que definimos como nível 01. Como não criaremos itens elementares nesse registro, especificamos a picture no próprio nível 01, indicando que o registro é um campo alfanumérico com 80 posições.
Definindo variáveis de apoio
Pelo que vimos até aqui já sabemos que nosso programa precisará de algumas variáveis de trabalho para apoiar a geração do relatório. A primeira delas é o file status do arquivo de entrada:
*================================================================*
WORKING-STORAGE SECTION.
*----------------------------------------------------------------*
01 WT-FILE-STATUS.
03 WT-ST-CRA0207V PIC X(002) VALUE SPACES.
Relatórios tradicionalmente não usam file status, mesmo que nesse caso estejamos gerando nosso relatório como um arquivo texto em disco.
Vimos que nosso relatório terá um rodapé que mostrará a quantidade de registros lidos/listados. Para isso teremos que criar uma variável de trabalho para contar esses registros. Além disso, o layout nos mostra que todas as páginas serão numeradas, e por isso precisamos também de uma variável para contar as páginas impressas.
*================================================================* WORKING-STORAGE SECTION. *----------------------------------------------------------------* 01 WT-FILE-STATUS. 03 WT-ST-CRA0207V PIC X(002) VALUE SPACES. 01 WT-CONTADORES. 03 WT-CT-LIDOS PIC 9(006) VALUE ZEROS. 03 WT-CT-PAGINA PIC 9(003) VALUE ZEROS.
Cada página terá cinco linhas de cabeçalho, contando com a linha tracejada para separação. A última página terá também um rodapé com 4 linhas (duas linhas separadoras e duas linhas com labels e totais). Se vamos adotar a convenção de 60 linhas por página, então precisamos garantir que cada página receba apenas 51 linhas de detalhe: 5 linhas de cabeçalho mais 51 linhas de detalhe mais 4 linhas de rodapé totalizam 60 linhas numa página).
Para contar a quantidade de linhas impressas precisaremos de mais uma variável de trabalho:
*================================================================*
WORKING-STORAGE SECTION.
*----------------------------------------------------------------*
01 WT-FILE-STATUS.
03 WT-ST-CRA0207V PIC X(002) VALUE SPACES.
01 WT-CONTADORES.
03 WT-CT-LIDOS PIC 9(006) VALUE ZEROS.
03 WT-CT-PAGINA PIC 9(003) VALUE ZEROS.
03 WT-CT-LINHAS PIC 9(002) VALUE 99.
E porque iniciamos essa variável com 99? O cabeçalho será impresso numa nova página toda vez que esse contador ultrapassar o limite de 60 linhas. Logo, para garantir que o cabeçalho apareça também na primeira página, precisamos garantir que esse contador já comece “estourado”. Poderíamos usar qualquer valor superior a 60 para iniciar essa variável.
O rodapé do nosso relatório mostrará também o somatório dos valores das duplicatas listadas. Precisamos então de uma variável para acumular esses valores, e vamos criá-la num item de grupo separado apenas para destacar que trata-se de um acumulador:
*================================================================* WORKING-STORAGE SECTION. *----------------------------------------------------------------* 01 WT-FILE-STATUS. 03 WT-ST-CRA0207V PIC X(002) VALUE SPACES. 01 WT-CONTADORES. 03 WT-CT-LIDOS PIC 9(006) VALUE ZEROS. 03 WT-CT-PAGINA PIC 9(003) VALUE ZEROS. 03 WT-CT-LINHAS PIC 9(002) VALUE 60. 01 WT-ACUMULADORES. 03 WT-AC-DUPLICATAS PIC S9(013)V9(002) VALUE ZEROS.
Sabemos que as datas de emissão e vencimento do arquivo de entrada estão no formato AAAAMMDD. Queremos mostra-las em nosso relatório no formato padrão e editado: DD/MM/AA. Para isso usaremos uma subrotina que transforma uma data AAAAMMD em DD/MM/AAAA. Na nossa empresa fictícia essa subrotina se chama XXREDT4. Por isso vamos criar variáveis para guardar seu nome (e assim chamá-la dinamicamente na PROCEDURE) e seu código de retorno (para validarmos se sua chamada foi bem sucedida).
*================================================================* WORKING-STORAGE SECTION. *----------------------------------------------------------------* 01 WT-FILE-STATUS. 03 WT-ST-CRA0207V PIC X(002) VALUE SPACES. 01 WT-CONTADORES. 03 WT-CT-LIDOS PIC 9(006) VALUE ZEROS. 03 WT-CT-PAGINA PIC 9(003) VALUE ZEROS. 03 WT-CT-LINHAS PIC 9(002) VALUE 60. 01 WT-ACUMULADORES. 03 WT-AC-DUPLICATAS PIC S9(013)V9(002) VALUE ZEROS. 01 WT-AUXILIARES. 03 WT-NM-EDT4 PIC X(008) VALUE "XXREDT4". 03 WT-RC-EDT4 PIC 9(002) VALUE ZEROS.
Agora só faltam mais duas variáveis de apoio: uma para recebermos a data corrente do sistema e outra para a hora corrente do sistema. Usaremos essas variáveis para completar as informações do cabeçalho de nosso relatório:
*================================================================* WORKING-STORAGE SECTION. *----------------------------------------------------------------* 01 WT-FILE-STATUS. 03 WT-ST-CRA0207V PIC X(002) VALUE SPACES. 01 WT-CONTADORES. 03 WT-CT-LIDOS PIC 9(006) VALUE ZEROS. 03 WT-CT-PAGINA PIC 9(003) VALUE ZEROS. 03 WT-CT-LINHAS PIC 9(002) VALUE 60. 01 WT-ACUMULADORES. 03 WT-AC-DUPLICATAS PIC S9(013)V9(002) VALUE ZEROS. 01 WT-AUXILIARES. 03 WT-NM-FDAT PIC X(008) VALUE "XXRFDAT". 03 WT-RC-FDAT PIC 9(002) VALUE ZEROS. 03 WT-DT-SISTEMA. 05 ANO PIC 9(002) VALUE ZEROS. 05 MES PIC 9(002) VALUE ZEROS. 05 DIA PIC 9(002) VALUE ZEROS. 03 WT-HR-SISTEMA. 05 HORA PIC 9(002) VALUE ZEROS. 05 MINUTO PIC 9(002) VALUE ZEROS. 05 SEGUNDO PIC 9(002) VALUE ZEROS.
Com isso temos todas as variáveis de trabalho que precisaremos na PROCEDURE. Agora podemos codificar o layout do relatório.
Codificando o layout do relatório
Linhas de cabeçalho e separação, linhas de detalhe e rodapés também são codificados na WORKING-STORAGE SECTION. Valores fixos (como os labels que contêm nome da empresa e nome do sistema) nunca serão mencionados diretamente na PROCEDURE DIVISION e por isso sempre usaremos FILLERs para declará-los. Apenas as variáveis que serão preenchidas durante o processamento receberão algum nome. Seguindo a convenção que estamos adotando nesse livro, toda variável de trabalho relacionada a relatórios terão o prefixo WR- em seus nomes.
Vamos analisar detalhadamente a primeira linha de cabeçalho, pois as técnicas que veremos aqui serão as mesmas que usaremos nas demais linhas de nosso layout.
A primeira linha do cabeçalho mostrará a string “DATA: 99/99/99” alinhada à direita. Sabemos que cada linha de nosso relatório terá no máximo 80 caracteres. A string “DATA: 99/99/99” tem 14. Logo, para que ela termine exatamente na posição 80, precisamos fazer com que antes dela exista um ou mais campos com exatamente 66 caracteres (80 menos 14).
O campo que vem antes dessa string é o nome da empresa (EMPRESA XYZ). Portanto, basta colocarmos o nome da empresa num FILLER de 66 posições e logo em seguida declararmos a string “DATA: “.
01 WR-CAB1. 03 FILLER PIC X(066) VALUE "EMPRESA XYZ". 03 FILLER PIC X(006) VALUE "DATA: ".
Agora precisamos inserir a variável que receberá a data de impressão (a data corrente do sistema). Para isso criaremos um item de grupo que será formado por dia, mês e ano, com uma barra de separação entre eles:
01 WR-CAB1. 03 FILLER PIC X(066) VALUE "EMPRESA XYZ". 03 FILLER PIC X(006) VALUE "DATA: ". 03 WR-CAB-DATA. 05 DIA PIC 9(002) VALUE ZEROS. 05 FILLER PIC X(001) VALUE "/". 05 MES PIC 9(002) VALUE ZEROS. 05 FILLER PIC X(001) VALUE "/". 05 ANO PIC 9(002) VALUE ZEROS.
Como teremos que fazer referência a essa variável na PROCEDURE, precisamos dar um nome para ela. Escolhemos WR-CAB-DATA pois isso deixará claro que se trata de uma variável para relatório (WR) existente num cabeçalho (CAB). Seus itens elementares receberam simplesmente os nomes DIA, MES e ANO.
Talvez você tenha notado que também usamos os nomes DIA, MES e ANO quando criamos a variável de trabalho que receberá a data do sistema. Isso não foi por acaso. Mais adiante veremos um novo formato do comando MOVE que facilita a movimentação de itens de grupo.
Entre os itens elementares DIA, MES e ANO colocamos dois FILLERs para posicionar as barras.
A segunda linha do cabeçalho segue a mesma lógica:
A única diferença é que teremos um campo para receber a hora da impressão (hora corrente do sistema):
01 WR-CAB2. 03 FILLER PIC X(066) VALUE "CONTAS A RECEBER". 03 FILLER PIC X(006) VALUE "HORA: ". 03 WR-CAB-HORA. 05 HOR PIC 9(002) VALUE ZEROS. 05 FILLER PIC X(001) VALUE ":". 05 MINUTO PIC 9(002) VALUE ZEROS. 05 FILLER PIC X(001) VALUE ":". 05 SEGUNDO PIC 9(002) VALUE ZEROS.
A terceira linha do cabeçalho é um pouco diferente porque tem um título que precisa ser centralizado. O título (DUPLICATAS VÁLIDAS) tem 18 caracteres. Logo, para centralizá-lo, precisamos colocar 31 caracteres de cada lado: (80 – 18) / 2.
O nome do relatório, portanto, ficará num FILLER com 31 caracteres:
01 WR-CAB3. 03 FILLER PIC X(031) VALUE "CRL0208".
O layout mostra que a palavra “PAGINA: “ precisa estar alinhada com as palavras “DATA:” e “HORA:” das linhas de cima. Logo, para completar os 66 caracteres criamos o título do relatório com um FILLER de 35:
01 WR-CAB3. 03 FILLER PIC X(031) VALUE "CRL0208". 03 FILLER PIC X(035) VALUE "DUPLICATAS VÁLIDAS".
E completamos com o label “PÁGINA: “ e a variável que exibirá o número das páginas:
01 WR-CAB3. 03 FILLER PIC X(031) VALUE "CRL0208". 03 FILLER PIC X(035) VALUE "DUPLICATAS VÁLIDAS". 03 FILLER PIC X(011) VALUE "PAGINA: ". 03 WR-CAB-PAGINA PIC ZZ9 VALUE ZEROS.
Agora repare na picture que usamos para a variável WR-CAB-PAGINA. Ela é chamada de “picture de edição” e é usada quando precisamos criar máscaras para campos que serão exibidos na tela ou impressos em relatórios. O símbolo “Z” numa picture indica que um zero nessa posição deve ser substituído por um espaço em branco; PIC ZZ9 estabelece um campo numérico editado de três dígitos com supressão de zeros à esquerda.
Variáveis com pictures de edição são usadas apenas para exibição (usage display) e não podem participar de operações aritméticas. Por isso tivemos que criar uma variável auxiliar na WORKING-STORAGE, chamada WT-CT-PAGINA. A cada impressão de um novo cabeçalho nós incrementaremos essa variável auxiliar e em seguida a movimentaremos para WR-CAB-PAGINA.
A próxima linha do nosso cabeçalho é apenas uma linha separadora, preenchida com hífens. Ela também será utilizada na impressão do rodapé, por isso lhe daremos um nome diferente: WR-SEP1. A cláusula VALUE ALL permite que se preencha toda a variável com um único caracter.
01 WR-SEP1 PIC X(080) VALUE ALL “-“.
Como todas as linhas do nosso relatório são itens de grupo, faremos a mesma coisa com o separador apenas para manter a coerência visual:
01 WR-SEP1.
03 FILLER PIC X(080) VALUE ALL “-“.
A quarta linha do cabeçalho contém os nomes das colunas. Para simplificar, vamos alinhá-las à esquerda dos campos, com exceção da coluna valor, que alinharemos à direita:
01 WR-CAB4. 03 FILLER PIC X(012) VALUE "DUPLICATA". 03 FILLER PIC X(010) VALUE "CLIENTE". 03 FILLER PIC X(013) VALUE "EMISSAO". 03 FILLER PIC X(018) VALUE "VENCIMENTO". 03 FILLER PIC X(008) VALUE "VALOR". 03 FILLER PIC X(003) VALUE "CAT".
Para a linha de detalhe usaremos o prefixo WR-DET- no nome das variáveis. Também usaremos FILLERs para criar os espaços entre os campos:
01 WR-DET1. 03 WR-DET-NR-FATURA PIC 9(006) VALUE ZEROS. 03 FILLER PIC X(001) VALUE "/". 03 WR-DET-NR-DUPLICATA PIC 9(002) VALUE ZEROS. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-CD-CLIENTE PIC X(006) VALUE SPACES. 03 FILLER PIC X(004) VALUE SPACES. 03 WR-DET-DT-EMISSAO PIC 99/99/9999 VALUE ZEROS. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-DT-VENCIMENTO PIC 99/99/9999 VALUE ZEROS. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-VL-FATURA PIC ZZZ.ZZ9,99 VALUE ZEROS. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-CD-CATEGORIA PIC X(003) VALUE SPACES.
É muito comum utilizarmos a picture de edição 99/99/9999 para datas. Isso é o ideal quando queremos mover uma data armazenada em campo numérico que esteja no formato DDMMAAAA. Mas no nosso exemplo, as datas estão no formato invertido (AAAAMMDD). Logo, se a linha detalhe ficar do jeito que está nossas datas aparecerão assim: 20/16/1025 ou 20/15/1102…
Por isso, mais adiante na PROCEDURE, usaremos uma subrotina que converte formatos de datas. Ela recebe uma variável numérica que tenha uma data no formato AAAAMMDD e gera uma variável alfanumérica que com a mesma data no formato DD/MM/AAAA. As datas na linha de detalhe, portanto, precisam adotar esse formato:
01 WR-DET1. 03 WR-DET-NR-FATURA PIC 9(006) VALUE ZEROS. 03 FILLER PIC X(001) VALUE "/". 03 WR-DET-NR-DUPLICATA PIC 9(002) VALUE ZEROS. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-CD-CLIENTE PIC X(006) VALUE SPACES. 03 FILLER PIC X(004) VALUE SPACES. 03 WR-DET-DT-EMISSAO PIC X(010) VALUE SPACES. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-DT-VENCIMENTO PIC X(010) VALUE SPACES. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-VL-FATURA PIC ZZZ.ZZ9,99 VALUE ZEROS. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-CD-CATEGORIA PIC X(003) VALUE SPACES.
Repare agora a picture de edição da variável valor. Esse é um formato muito comum quando queremos exibir valores monetários. A picture da variável CRA0207V-VL-FATURA é S9(013)V9(002), já a variável WR-DET-VL-FATURA está com formato ZZZ.ZZ9,99, com apenas seis dígitos na parte inteira. Se quiséssemos que WR-DET-VL-FATURA tivesse o mesmo tamanho de CRA0207V-VL-FATURA, teríamos que criar a variável assim:
03 WR-DET-VL-FATURA PIC Z.ZZZ.ZZZ.ZZZ.ZZ9,99 VALUE ZEROS.
No contexto desse exemplo, porém, não temos valores de duplicatas superiores a R$ 999.999,99, logo optamos por usar uma picture menor. O símbolo Z, como já vimos, faz com que o COBOL substitua zeros não significativos por um espaço em branco.
Para terminar a definição do relatório só faltam as linhas do rodapé, que aparecerão na última página e que mostrarão a quantidade de registros listados e o somatório do valor das duplicatas:
As técnicas que usamos aqui para calcular o tamanho dos FILLERs são as mesmas que mostramos na definição das linhas anteriores:
01 WR-ROD1. 03 FILLER PIC X(054) VALUE SPACES. 03 FILLER PIC X(020) VALUE "DUPLICATAS LISTADAS:". 03 WR-ROD-CT-LIDOS PIC ZZ.ZZ9 VALUE ZEROS. 01 WR-ROD2. 03 FILLER PIC X(054) VALUE SPACES. 03 FILLER PIC X(014) VALUE "VALOR TOTAL: ". 03 WR-ROD-AC-DUPLICATAS PIC Z.ZZZ.ZZ9,99 VALUE ZEROS.
Agora sim estamos prontos para codificar a PROCEDURE que é onde o relatório será realmente impresso. Antes disso, vamos ver como ficou nossa WORKING-STORAGE completa:
*================================================================* WORKING-STORAGE SECTION. *----------------------------------------------------------------* 01 WT-FILE-STATUS. 03 WT-ST-CRA0207V PIC X(002) VALUE SPACES. 01 WT-CONTADORES. 03 WT-CT-LIDOS PIC 9(006) VALUE ZEROS. 03 WT-CT-PAGINA PIC 9(003) VALUE ZEROS. 03 WT-CT-LINHAS PIC 9(002) VALUE 60. 01 WT-ACUMULADORES. 03 WT-AC-DUPLICATAS PIC S9(013)V9(002) VALUE ZEROS. 01 WT-AUXILIARES. 03 WT-NM-EDT4 PIC X(008) VALUE "XXREDT4". 03 WT-RC-EDT4 PIC 9(002) VALUE ZEROS. 03 WT-DT-SISTEMA. 05 ANO PIC 9(002) VALUE ZEROS. 05 MES PIC 9(002) VALUE ZEROS. 05 DIA PIC 9(002) VALUE ZEROS. 03 WT-HR-SISTEMA. 05 HORA PIC 9(002) VALUE ZEROS. 05 MINUTO PIC 9(002) VALUE ZEROS. 05 SEGUNDO PIC 9(002) VALUE ZEROS. 01 WR-CAB1. 03 FILLER PIC X(066) VALUE "EMPRESA XYZ". 03 FILLER PIC X(006) VALUE "DATA: ". 03 WR-CAB-DATA. 05 DIA PIC 9(002) VALUE ZEROS. 05 FILLER PIC X(001) VALUE "/". 05 MES PIC 9(002) VALUE ZEROS. 05 FILLER PIC X(001) VALUE "/". 05 ANO PIC 9(002) VALUE ZEROS. 01 WR-CAB2. 03 FILLER PIC X(066) VALUE "CONTAS A RECEBER". 03 FILLER PIC X(006) VALUE "HORA: ". 03 WR-CAB-HORA. 05 HORA PIC 9(002) VALUE ZEROS. 05 FILLER PIC X(001) VALUE ":". 05 MINUTO PIC 9(002) VALUE ZEROS. 05 FILLER PIC X(001) VALUE ":". 05 SEGUNDO PIC 9(002) VALUE ZEROS. 01 WR-CAB3. 03 FILLER PIC X(031) VALUE "CRL0208". 03 FILLER PIC X(035) VALUE "DUPLICATAS VALIDAS". 03 FILLER PIC X(011) VALUE "PAGINA: ". 03 WR-CAB-PAGINA PIC ZZ9 VALUE ZEROS. 01 WR-SEP1. 03 FILLER PIC X(080) VALUE ALL "-". 01 WR-CAB4. 03 FILLER PIC X(012) VALUE "DUPLICATA". 03 FILLER PIC X(010) VALUE "CLIENTE". 03 FILLER PIC X(013) VALUE "EMISSAO". 03 FILLER PIC X(018) VALUE "VENCIMENTO". 03 FILLER PIC X(008) VALUE "VALOR". 03 FILLER PIC X(009) VALUE "CATEGORIA". 01 WR-DET1. 03 WR-DET-NR-FATURA PIC 9(006) VALUE ZEROS. 03 FILLER PIC X(001) VALUE "/". 03 WR-DET-NR-DUPLICATA PIC 9(002) VALUE ZEROS. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-CD-CLIENTE PIC X(006) VALUE SPACES. 03 FILLER PIC X(004) VALUE SPACES. 03 WR-DET-DT-EMISSAO PIC X(010) VALUE SPACES. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-DT-VENCIMENTO PIC X(010) VALUE SPACES. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-VL-FATURA PIC ZZZ.ZZ9,99 VALUE ZEROS. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-CD-CATEGORIA PIC X(003) VALUE SPACES. 01 WR-ROD1. 03 FILLER PIC X(054) VALUE SPACES. 03 FILLER PIC X(020) VALUE "DUPLICATAS LISTADAS:". 03 WR-ROD-CT-LIDOS PIC ZZ.ZZ9 VALUE ZEROS. 01 WR-ROD2. 03 FILLER PIC X(054) VALUE SPACES. 03 FILLER PIC X(014) VALUE "VALOR TOTAL: ". 03 WR-ROD-AC-DUPLICATAS PIC Z.ZZZ.ZZ9,99 VALUE ZEROS.
Codificando o programa
Nossa PROCEDURE será dividida em quatro parágrafos, como mostramos na figura abaixo:
No parágrafo INICIA colocaremos a abertura dos arquivos e a primeira leitura do arquivo de entrada.
No parágrafo IMPRIME-RELATORIO ficará a impressão da linha de detalhe. Esse parágrafo será executado para todos os registros do arquivo de entrada. Cada vez que o contador de linhas ultrapassar o limite de 60 linhas por página o programa chamará o parágrafo para impressão de cabeçalho.
Depois que todas as linhas forem impressas, o parágrafo IMPRIME-RODAPE será chamado para imprimir os totais. Pode ser que o parágrafo de impressão de cabeçalhos também tenha que ser chamado aqui, caso o contador de linhas também ultrapasse o limite de linhas por página.
O parágrafo TERMINA, terá os comandos para fechamento de arquivo e retirada da subrotina de memória.
O início da PROCEDURE portanto só precisa chamar os quatro parágrafos principais e encerrar o programa:
*================================================================* PROCEDURE DIVISION. *----------------------------------------------------------------* 0-PRINCIPAL. PERFORM 1-INICIA PERFORM 2-IMPRIME-RELATORIO UNTIL WT-ST-CRA0207V NOT = "00" PERFORM 3-IMPRIME-RODAPE PERFORM 4-TERMINA STOP RUN. *----------------------------------------------------------------* * ABRE ARQUIVOS E LE PRIMEIRO REGISTRO DO ARQUIVO DE ENTRADA *----------------------------------------------------------------* 1-INICIA. OPEN INPUT CRA0207V OPEN OUTPUT CRL0208 READ CRA0207V.
A primeira coisa que faremos no parágrafo de impressão do relatório é testar o contador de linhas. Se ele estiver com um valor maior que 60 chamaremos o parágrafo que imprime cabeçalho:
*----------------------------------------------------------------* * LISTA TODOS OS REGISTROS DO ARQUIVO DE ENTRADA *----------------------------------------------------------------* 2-IMPRIME-RELATORIO. IF WT-CT-LINHAS > 60 PERFORM X1-IMPRIME-CABECALHO END-IF
Você se lembra que quando criamos a variável WT-CT-LINHA nós estabelecemos para ela um valor inicial igual a 99. Isso fará com que o programa entre nesse IF antes que a primeira linha seja impressa, garantindo que o cabeçalho aparecerá na primeira página do relatório.
Logo em seguida usaremos o comando MOVE, já conhecido, para preencher todos os campos da linha de detalhe. As únicas exceções são os campos data de emissão e data de vencimento, pois para eles precisaremos chamar a subrotina XXREDT4 que vai transformar uma data numérica AAAAMMDD numa data alfanumérica DD/MM/AAAA. Os parâmetros serão passados “by reference” (por default), o que permitirá à subrotina ler e alterar diretamente as variáveis que estamos passando:
*----------------------------------------------------------------* * LISTA TODOS OS REGISTROS DO ARQUIVO DE ENTRADA *----------------------------------------------------------------* 2-IMPRESSAO-RELATORIO. IF WT-CT-LINHAS > 57 PERFORM X1-IMPRESSAO-CABECALHO END-IF MOVE CRA0207V-NR-FATURA TO WR-DET-NR-FATURA MOVE CRA0207V-NR-DUPLICATA TO WR-DET-NR-DUPLICATA MOVE CRA0207V-CD-CLIENTE TO WR-DET-CD-CLIENTE CALL WT-NM-EDT4 USING CRA0207V-DT-EMISSAO WR-DET-DT-EMISSAO WT-RC-EDT4 CALL WT-NM-EDT4 USING CRA0207V-DT-VENCIMENTO WR-DET-DT-VENCIMENTO WT-RC-EDT4 MOVE CRA0207V-VL-FATURA TO WR-DET-VL-FATURA MOVE CRA0207V-CD-CATEGORIA TO WR-DET-CD-CATEGORIA
O comando que usaremos para “imprimir” a linha de detalhe é praticamente o mesmo que usamos para gravar registros em arquivos de saída, em programas anteriores: WRITE. Podemos codificá-lo assim:
MOVE WR-DET TO CRL0208-REGISTRO WRITE CRL0208-REGISTRO
No entanto, usaremos a opção FROM para eliminar esse MOVE:
WRITE CRL0208-REGISTRO FROM WR-DET
Algumas vezes precisamos imprimir alguma coisa saltando uma determinada quantidade de linhas. Na prática, o COBOL insere um conjunto de caracteres LF (ou LF+CR, no caso do Windows) que faz com a linha seja impressa depois de “x” linhas em branco. Seria o caso do exemplo abaixo:
WRITE CRL0208-REGISTRO FROM WR-DET AFTER 5 LINES
Mas no nosso caso queremos apenas uma linha embaixo da outra, o que seria equivalente a “AFTER 1 LINE”. Como essa é a opção default não vamos nos preocupar em codificá-la.
Agora que já imprimimos a linha de detalhe, vamos incrementar os contadores de registros lidos e linhas impressas e acumular o valor da duplicata. O último comando do parágrafo será a leitura do próximo registro do arquivo de entrada:
*----------------------------------------------------------------* * LISTA TODOS OS REGISTROS DO ARQUIVO DE ENTRADA *----------------------------------------------------------------* 2-IMPRESSAO-RELATORIO. IF WT-CT-LINHAS > 57 PERFORM X1-IMPRESSAO-CABECALHO END-IF MOVE CRA0207V-NR-FATURA TO WR-DET-NR-FATURA MOVE CRA0207V-NR-DUPLICATA TO WR-DET-NR-DUPLICATA MOVE CRA0207V-CD-CLIENTE TO WR-DET-CD-CLIENTE CALL WT-NM-EDT4 USING CRA0207V-DT-EMISSAO WR-DET-DT-EMISSAO WT-RC-EDT4 CALL WT-NM-EDT4 USING CRA0207V-DT-VENCIMENTO WR-DET-DT-VENCIMENTO WT-RC-EDT4 MOVE CRA0207V-VL-FATURA TO WR-DET-VL-FATURA MOVE CRA0207V-CD-CATEGORIA TO WR-DET-CD-CATEGORIA WRITE CRL0208-REGISTRO FROM WR-DET1 ADD 1 TO WT-CT-LINHAS ADD 1 TO WT-CT-LIDOS ADD CRA0207V-VL-FATURA TO WT-AC-DUPLICATAS READ CRA0207V.
Isso é tudo o que precisamos para imprimir o relatório. Agora construiremos o terceiro parágrafo, responsável pela impressão do rodapé. Lembre-se que uma das boas práticas de programação estruturada é criar os parágrafos na mesma ordem em que eles aparecem nos comandos PERFORM. Logo, depois de codificar o parágrafo 2- é hora de codificar o 3-:
*----------------------------------------------------------------* * IMPRIME RODAPE' *----------------------------------------------------------------* 3-IMPRIME-RODAPE. IF WT-CT-LINHAS > 60 PERFORM X1-IMPRIME-CABECALHO END-IF MOVE WT-CT-LIDOS TO WR-ROD-CT-LIDOS MOVE WT-AC-DUPLICATAS TO WR-ROD-AC-DUPLICATAS WRITE CRL0208-REGISTRO FROM WR-SEP1 WRITE CRL0208-REGISTRO FROM WR-ROD1 WRITE CRL0208-REGISTRO FROM WR-ROD2 WRITE CRL0208-REGISTRO FROM WR-SEP1.
Repare que ele também testa o contador de linhas impressas e chama o parágrafo de impressão de cabeçalhos caso esse contador esteja maior que o limite de linhas por página.
Os únicos campos que precisamos movimentar aqui é o contador e o acumulador. Os comandos WRITE para impressão são iguais aos que já vimos. A diferença é que precisamos imprimir a linha WR-SEP1 duas vezes para garantir que o rodapé terá duas linhas tracejadas, como previsto pelo layout.
Logo em seguida, codificamos o parágrafo para término do programa, muito semelhante ao que fizemos nos exemplos anteriores…
*----------------------------------------------------------------* * FECHA ARQUIVOS E EXIBE OS CONTADORES DE REGISTROS LIDOS E IM- * PRESSOS *----------------------------------------------------------------* 4-TERMINO. CANCEL WT-NM-EDT4 CLOSE CRA0207V CRL0208.
E finalmente o último parágrafo, onde o cabeçalho será impresso:
*----------------------------------------------------------------* * IMPRIME CABECALHO *----------------------------------------------------------------* X1-IMPRESSAO-CABECALHO. ACCEPT WT-DT-SISTEMA FROM DATE ACCEPT WT-HR-SISTEMA FROM TIME ADD 1 TO WT-CT-PAGINA MOVE CORR WT-DT-SISTEMA TO WR-CAB-DATA MOVE CORR WT-HR-SISTEMA TO WR-CAB-HORA MOVE WT-CT-PAGINA TO WR-CAB-PAGINA WRITE CRL0208-REGISTRO FROM WR-CAB1 AFTER PAGE WRITE CRL0208-REGISTRO FROM WR-CAB2 WRITE CRL0208-REGISTRO FROM WR-CAB3 WRITE CRL0208-REGISTRO FROM WR-SEP1 WRITE CRL0208-REGISTRO FROM WR-CAB4 WRITE CRL0208-REGISTRO FROM WR-SEP1 MOVE 6 TO WT-CT-LINHAS.
Usamos um comando novo para obter a data e a hora do sistema: ACCEPT. Existem dezenas de formatos diferentes para o comando ACCEPT, pois ele pode ser usado tanto para recuperar informações do sistema operacional (como data e hora corrente) quanto para obter argumentos passados ao programa pela linha de comando, ou informações fornecidas pelo usuário via teclado, ou mesmo telas inteiras…
Por enquanto, nos interessa apenas saber que ACCEPT WT-DT-SISTEMA FROM DATE, preenche a variável (WT-DT-SISTEMA) com a data do sistema, que vem no formato AAMMDD. O comando ACCEPT WT-HR-SISTEMA FROM TIME preenche a variável informada com a hora corrente do sistema, que vem no formato HHMMSS.
Depois de somar 1 ao contador de páginas, usamos mais um comando novo: MOVE CORR. Você se lembra que criamos na WORKING a variável WT-DT-SISTEMA para armazenar a data do sistema, e que ela tinha a seguinte estrutura:
03 WT-DT-SISTEMA. 05 ANO PIC 9(002) VALUE ZEROS. 05 MES PIC 9(002) VALUE ZEROS. 05 DIA PIC 9(002) VALUE ZEROS.
E lembra também que criamos uma variável para mostrar a data no cabeçalho usando itens elementares que tinham o mesmo nome:
03 WR-CAB-DATA. 05 DIA PIC 9(002) VALUE ZEROS. 05 FILLER PIC X(001) VALUE "/". 05 MES PIC 9(002) VALUE ZEROS. 05 FILLER PIC X(001) VALUE "/". 05 ANO PIC 9(002) VALUE ZEROS.
O que o comando “MOVE CORR item-grupo-1 TO item-grupo-2” faz é copiar apenas os valores dos itens elementares que tenham nomes iguais, tanto em item-grupo-1 quanto em item-grupo-2. E esses itens elementares não precisam estar na mesma ordem. Logo, na prática ele inverteu a data do sistema e a colocou num formato legível no cabeçalho. Usamos o mesmo recurso para movimentar a hora corrente do sistema.
Outro detalhe importante: repare que usamos a opção AFTER PAGE para imprimir a primeira linha do cabeçalho. Isso insere um caracter especial que força a impressora a fazer a avançar uma página antes de continuar a impressão; a mesma função do page break que inserimos nos editores de texto.
Depois que o cabeçalho é impresso, precisamos reiniciar o contador de linhas por página. Nós movemos 6 para esse contador porque, nesse momento, é essa a quantidade de linhas que foram impressas na nova página.
E está pronto. Depois de compilar, linkeditar e executar (apontando naturalmente para um arquivo CRA0207V que exista) o resultado será esse:
EMPRESA XYZ DATA: 09/01/17 CONTAS A RECEBER HORA: 15:17:30 CRL0208 DUPLICATAS VALIDAS PAGINA: 1 -------------------------------------------------------------------------------- DUPLICATA CLIENTE EMISSAO VENCIMENTO VALOR CATEGORIA -------------------------------------------------------------------------------- 001324/01 000015 25/10/2016 25/11/2016 123,45 SLA 001325/01 000010 25/10/2016 25/11/2016 2.319,00 SLA 001326/01 000009 25/10/2016 25/11/2016 590,00 SLA 001326/02 000009 25/10/2016 25/11/2016 590,00 SLA 001326/03 000009 25/10/2016 25/11/2016 590,00 SLA 001326/04 000009 25/10/2016 25/11/2016 590,00 SLA 001326/05 000009 25/10/2016 25/11/2016 590,00 SLA 001327/01 000010 25/10/2016 25/11/2016 4.850,00 SLA 001328/01 000011 25/10/2016 25/11/2016 1.810,15 SLA 001329/01 000008 25/10/2016 25/11/2016 5.650,00 SLA 001330/01 000005 25/10/2016 25/11/2016 45,00 SLA 001331/01 000004 25/10/2016 25/11/2016 2.100,00 SLA 001332/01 000003 25/10/2016 25/11/2016 510,00 SLA 001333/01 000003 25/10/2016 25/11/2016 2.740,00 SLA 001333/02 000003 25/10/2016 25/11/2016 2.740,00 SLA -------------------------------------------------------------------------------- DUPLICATAS LISTADAS: 15 VALOR TOTAL: 25.837,60 --------------------------------------------------------------------------------
O programa completo
O trecho abaixo mostra a PROCEDURE DIVISION completa do programa que acabamos de construir:
*================================================================* PROCEDURE DIVISION. *----------------------------------------------------------------* 0-PRINCIPAL. PERFORM 1-INICIA PERFORM 2-IMPRIME-RELATORIO UNTIL WT-ST-CRA0207V NOT = "00" PERFORM 3-IMPRIME-RODAPE PERFORM 4-TERMINA STOP RUN. *----------------------------------------------------------------* * ABRE ARQUIVOS E LE PRIMEIRO REGISTRO DO ARQUIVO DE ENTRADA *----------------------------------------------------------------* 1-INICIA. OPEN INPUT CRA0207V OPEN OUTPUT CRL0208 READ CRA0207V. *----------------------------------------------------------------* * LISTA TODOS OS REGISTROS DO ARQUIVO DE ENTRADA *----------------------------------------------------------------* 2-IMPRIME-RELATORIO. IF WT-CT-LINHAS > 60 PERFORM X1-IMPRIME-CABECALHO END-IF MOVE CRA0207V-NR-FATURA TO WR-DET-NR-FATURA MOVE CRA0207V-NR-DUPLICATA TO WR-DET-NR-DUPLICATA MOVE CRA0207V-CD-CLIENTE TO WR-DET-CD-CLIENTE CALL WT-NM-EDT4 USING CRA0207V-DT-EMISSAO WR-DET-DT-EMISSAO WT-RC-EDT4 CALL WT-NM-EDT4 USING CRA0207V-DT-VENCIMENTO WR-DET-DT-VENCIMENTO WT-RC-EDT4 MOVE CRA0207V-VL-FATURA TO WR-DET-VL-FATURA MOVE CRA0207V-CD-CATEGORIA TO WR-DET-CD-CATEGORIA WRITE CRL0208-REGISTRO FROM WR-DET1 ADD 1 TO WT-CT-LINHAS ADD 1 TO WT-CT-LIDOS ADD CRA0207V-VL-FATURA TO WT-AC-DUPLICATAS READ CRA0207V. *----------------------------------------------------------------* * IMPRIME RODAPE' *----------------------------------------------------------------* 3-IMPRIME-RODAPE. IF WT-CT-LINHAS > 57 PERFORM X1-IMPRESSAO-CABECALHO END-IF MOVE WT-CT-LIDOS TO WR-ROD-CT-LIDOS MOVE WT-AC-DUPLICATAS TO WR-ROD-AC-DUPLICATAS WRITE CRL0208-REGISTRO FROM WR-SEP1 WRITE CRL0208-REGISTRO FROM WR-ROD1 WRITE CRL0208-REGISTRO FROM WR-ROD2 WRITE CRL0208-REGISTRO FROM WR-SEP1. *----------------------------------------------------------------* * FECHA ARQUIVOS E EXIBE OS CONTADORES DE REGISTROS LIDOS E IM- * PRESSOS *----------------------------------------------------------------* 4-TERMINA. CANCEL WT-NM-EDT4 CLOSE CRA0207V CRL0208. *----------------------------------------------------------------* * IMPRIME CABECALHO *----------------------------------------------------------------* X1-IMPRIME-CABECALHO. ACCEPT WT-DT-SISTEMA FROM DATE ACCEPT WT-HR-SISTEMA FROM TIME ADD 1 TO WT-CT-PAGINA MOVE CORR WT-DT-SISTEMA TO WR-CAB-DATA MOVE CORR WT-HR-SISTEMA TO WR-CAB-HORA MOVE WT-CT-PAGINA TO WR-CAB-PAGINA WRITE CRL0208-REGISTRO FROM WR-CAB1 AFTER PAGE WRITE CRL0208-REGISTRO FROM WR-CAB2 WRITE CRL0208-REGISTRO FROM WR-CAB3 WRITE CRL0208-REGISTRO FROM WR-SEP1 WRITE CRL0208-REGISTRO FROM WR-CAB4 WRITE CRL0208-REGISTRO FROM WR-SEP1 MOVE 6 TO WT-CT-LINHAS.
Anterior | Conteúdo | Próxima |