12. Trabalhando com arquivos indexados
Até aqui trabalhamos com arquivos sequenciais (ou mais precisamente arquivos sequenciais lineares), onde os registros são armazenados na ordem em que foram inseridos e que só podem ser lidos um a um. Esse tipo de arquivo é perfeito para o processamento em lote de grandes volumes de informação. Mas muitas vezes precisamos acessar registros de forma randômica – sem uma ordem pré-estabelecida – usando chaves ou índices.
Arquivos indexados permitem que seus registros sejam acessados diretamente a partir de uma ou mais chaves formadas por um ou mais campos do registro.
Fundamentos
Ao contrário da organização sequencial, que mantém todos os registros num só lugar, na organização indexada os arquivos são formados por dois componentes: uma área de dados (semelhante a um arquivo sequencial) e uma área de índices, onde os valores das chaves apontam para a localização física do registro na área de dados.
A área de índice é ordenada pelos valores das chaves que foram definidas para o arquivo. Já na área de dados, os registros são armazenados na ordem em que foram inseridos, sem nenhuma classificação pré-estabelecida.
Para ler, por exemplo, o cliente 789 da figura anterior, o programa procura o valor dessa chave (789) na área de índice, obtém a localização física do registro (14) e depois lê diretamente o conteúdo que está nessa posição, na área de dados.
Mas arquivos indexados também pode ser acessados em modo sequencial. Nesse caso, o programa lê sequencialmente a área de índices e para cada índice lido acessa a posição correspondente na área de dados. Consequentemente, mesmo que a área de dados não esteja ordenada, a leitura sequencial de um arquivo indexado sempre recuperará os registros na ordem ascendente do índice utilizado.
Todo arquivo indexado deve conter pelo menos uma chave, chamada primary key ou chave primária. Não podem existir dois registros num mesmo arquivo que possuam os mesmos valores no(s) campo(s) que compõe(m) a chave primária.
Além da chave primária, arquivos indexados podem ser definidos com chaves alternativas, também conhecidas como alternate keys. As chaves alternativas podem ser únicas (como as chaves primárias) mas também podem permitir a duplicidade de registros.
A figura acima mostra um campo chamado CD-SEGTO que foi definido como chave alternativa. Repare que a área de índices possui um mesmo valor desse campo sendo associado a mais de um registro.
Quando um programa acessa um arquivo indexado de forma randômica por uma chave alternativa que permita duplicidade, apenas o primeiro registro que possui essa chave é recuperado. Na figura anterior, se um programa tentasse ler o arquivo informando “SRV” para a chave CD-SEGTO, apenas o registro na posição 7 seria recuperado. Para acessar os demais registros o programa teria que continuar a leitura em modo sequencial.
Declarando arquivos indexados
Arquivos indexados são declarados na ENVIRONMENT DIVISION, como qualquer outro arquivo convencional. A diferença em relação às declarações que vimos até agora é que precisamos indicar qual é sua chave primária (obrigatória) e quais são suas chaves alternativas (opcionais). O exemplo abaixo mostra a declaração de um arquivo indexado que não tem chaves alternativas:
ENVIRONMENT DIVISION. CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT arquivo ASSIGN TO nome-externo ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS nome-do-campo1 FILE STATUS IS nome-da-variavel.
Já o exemplo abaixo mostra a declaração de um arquivo indexado que possui duas chaves alternativas:
ENVIRONMENT DIVISION. CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT arquivo ASSIGN TO nome-externo ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS nome-do-campo1 ALTERNATE RECORD KEY IS nome-do-campo2 ALTERNATE RECORD KEY IS nome-do-campo3 WITH DUPLICATES FILE STATUS IS nome-da-variavel.
A opção WITH DUPLICATES da cláusula ALTERNATE RECORD KEY informa que a chave permite duplicidade. Se essa opção for omitida, o programa assume que a chave é única.
A cláusula ACCESS MODE indica como o programa acessará o arquivo. Ela permite as seguintes \opções:
- ACCESS MODE IS SEQUENTIAL: O programa vai escolher uma das chaves (a primária ou uma das alternativas) e lerá os registros sequencialmente usando essa chave.
- ACCESS MODE IS RANDOM: O programa vai escolher uma das chaves (a primária ou uma das alternativas), informar um valor para essa chave e acessar diretamente (randomicamente) o primeiro registro associado a esse valor
- ACCESS MODE IS DYNAMIC: O programa poderá acessar o arquivo tanto no modo sequencial quanto no modo randômico.
Se a cláusula ACCESS MODE for omitida, o programa assume que o acesso será sequencial. O mais comum é que se crie um book para declarar o arquivo indexado usando o modo DYNAMIC, uma vez que com isso esse book poderia ser usado tanto por programas que acessem o arquivo em modo sequencial quanto por programas que precisem acessá-lo em modo randômico.
O uso do File Status em arquivos indexados
A variável associada à cláusula FILE STATUS na declaração do arquivo é igual às que criamos para arquivos sequenciais: alfanumérica de duas posições. Como também acontecia com os arquivos sequenciais, essa variável é atualizada a cada operação de I/O que realizamos sobre o arquivo, inclusive na sua abertura e fechamento.
Alguns dos erros mencionados acima podem ser recuperados pelo próprio programa. Em um programa on-line, por exemplo, é comum receber o erro 23 no comando READ que tentou ler uma chave que o usuário informou incorretamente.
Outros erros são irrecuperável e exigem que o programa interrompa o processamento. Por exemplo, um erro 39 significa que o programa declarou um arquivo indexado mas o que o programa encontrou no ambiente foi um arquivo com organização sequencial. Um erro 47 significa que o programa tentou fazer um READ num arquivo que foi aberto com OPEN OUTPUT… Essas situações, normalmente, são tratadas com uma mensagem de erro e um fim de processamento anormal.
Detalhando arquivos indexados
A file description (FD) de um arquivo indexado na DATA DIVISION é igual às que vimos até agora. O único ponto que merece atenção é que todos os campos mencionados na RECORD KEY ou na(s) ALTERNATE RECORD KEY(s) da cláusula SELECT precisam ser declarados explicitamente no layout do registro:
SELECT CRA0201 ASSIGN TO “$DAT/CRA0201” ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS CRA0201-CD-CLIENTE ALTERNATE RECORD KEY IS CRA0201-CD-SEGMENTO WITH DUPLICATES ALTERNATE RECORD KEY IS CRA0201-NR-CNPJ WITH DUPLICATES FILE STATUS IS WT-ST-CRA0201. ... FD CRA0201. 01 CRA0201-REGISTRO. 03 CRA0201-CD-CLIENTE PIC X(006). 03 CRA0201-NM-CLIENTE PIC X(045). 03 CRA0201-NM-LOGRADOURO PIC X(045). 03 CRA0201-NM-BAIRRO PIC X(020). 03 CRA0201-NM-CIDADE PIC X(020). 03 CRA0201-CD-ESTADO PIC X(002). 03 CRA0201-NR-TELEFONE-1 PIC X(020). 03 CRA0201-NR-TELEFONE-2 PIC X(020). 03 CRA0201-NM-EMAIL PIC X(030). 03 CRA0201-NR-CNPJ PIC 9(014). 03 CRA0201-CD-SEGMENTO PIC X(003).
É uma boa prática de programação destacar, na FD, quais são as chaves primárias e as chaves alternativas do arquivo, mesmo quando elas são formadas por itens elementares, como no exemplo acima. Um detalhamento melhor desse arquivo poderia ser:
SELECT CRA0201 ASSIGN TO “$DAT/CRA0201” ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS CRA0201-CHAVE-1 ALTERNATE RECORD KEY IS CRA0201-CHAVE-2 WITH DUPLICATES ALTERNATE RECORD KEY IS CRA0201-CHAVE-3 WITH DUPLICATES FILE STATUS IS WT-ST-CRA0201. ... FD CRA0201. 01 CRA0201-REGISTRO. 03 CRA0201-CHAVE-1. 05 CRA0201-CD-CLIENTE PIC X(006). 03 CRA0201-NM-CLIENTE PIC X(045). 03 CRA0201-NM-LOGRADOURO PIC X(045). 03 CRA0201-NM-BAIRRO PIC X(020). 03 CRA0201-NM-CIDADE PIC X(020). 03 CRA0201-CD-ESTADO PIC X(002). 03 CRA0201-NR-TELEFONE-1 PIC X(020). 03 CRA0201-NR-TELEFONE-2 PIC X(020). 03 CRA0201-NM-EMAIL PIC X(030). 03 CRA0201-CHAVE-2. 05 CRA0201-NR-CNPJ PIC 9(014). 03 CRA0201-CHAVE-3. 05 CRA0201-CD-SEGMENTO PIC X(003).
Chaves primárias e alternativas podem ser compostas, isto é, formadas por mais de um campo do registro. No exemplo abaixo, a chave primária do arquivo é formada pelos campos NR-FATURA e NR-DUPLICATA:
SELECT CRA0202 ASSIGN TO “$DAT/CRA0202” ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS CRA0202-CHAVE-1 FILE STATUS IS WT-ST-CRA0202. ... FD CRA0202. 01 CRA0202-REGISTRO. 03 CRA0202-CHAVE-1. 05 CRA0201-NR-FATURA PIC 9(006). 05 CRA0201-NR-DUPLICATA PIC 9(002). 03 CRA0201-CD-CLIENTE PIC X(006). 03 CRA0201-DT-EMISSAO PIC 9(008). 03 CRA0201-DT-VENCIMENTO PIC 9(008). 03 CRA0201-VL-DUPLICATA PIC S9(013)V9(002). 03 CRA0201-CD-CATEGORIA PIC X(003). 03 CRA0201-ST-DUPLICATA PIC X(003).
Lendo arquivos indexados em modo randômico
A leitura direta – aquela que traz um único registro associado a uma chave informada – é a forma mais simples de acesso a um arquivo indexado. Vamos ver como isso acontece alterando um dos programas que já construímos nesse livro: FAP0501.
Esse programa gerou o seguinte relatório:
EMPRESA XYZ DATA: 14/01/17 FATURAMENTO HORA: 09:29:37 FAL0501 VENDAS POR VENDEDOR PAGINA: 1 -------------------------------------------------------------------------------- VENDEDOR DATA CLIENTE FATURA VALOR -------------------------------------------------------------------------------- 000001 11/05/2015 001240 015001 21.250,00 11/05/2015 002304 015002 74.750,00 13/05/2015 000988 015005 25.790,00 ------------ TOTAL: 121.790,00 000002 11/05/2015 001700 015003 116.414,00 12/05/2015 002800 015004 8.515,00 ------------ TOTAL: 124.929,00 000003 14/05/2015 003501 015006 1.600,00 ------------ TOTAL: 1.600,00 000004 15/05/2015 000515 015007 35.300,00 15/05/2015 000789 015009 7.280,00 15/05/2015 000901 015011 41.200,00 15/05/2015 001330 015012 8.920,00 ------------ TOTAL: 92.700,00 000005 15/05/2015 001514 015008 6.665,00 15/05/2015 002201 015010 12.458,00 16/05/2015 001101 015013 1.740,00 ------------ TOTAL: 20.863,00 000006 16/05/2015 002005 015014 64.300,00 ------------ TOTAL: 64.300,00
Vamos alterar esse relatório para exibir o nome do cliente no lugar do código do cliente. Para isso, teremos que deslocar o número da fatura para a direita para abrir espaço para o novo campo. O novo layout do relatório ficará assim:
EMPRESA XYZ DATA: 99/99/99 FATURAMENTO HORA: 99:99:99 FAL0501 VENDAS POR VENDEDOR PAGINA: ZZ9 -------------------------------------------------------------------------------- VENDEDOR DATA CLIENTE FATURA VALOR -------------------------------------------------------------------------------- 999999 99/99/9999 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 999999 ZZZ.ZZ9,99 99/99/9999 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 999999 ZZZ.ZZ9,99 ------------ TOTAL: Z.ZZZ.ZZ9,99 999999 99/99/9999 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 999999 ZZZ.ZZ9,99 99/99/9999 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 999999 ZZZ.ZZ9,99 ------------ TOTAL: Z.ZZZ.ZZ9,99
O nome do cliente está num arquivo indexado chamado CRA0201. O book de declaração do arquivo se chama CRS0201, que tem o seguinte conteúdo:
*----------------------------------------------------------------* * CADASTRO DE CLIENTES *----------------------------------------------------------------* SELECT CRA0201 ASSIGN TO "$DAT/CRA0201.DAT" ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS CRA0201-CHAVE-1 ALTERNATE RECORD KEY IS CRA0201-CHAVE-2 WITH DUPLICATES ALTERNATE RECORD KEY IS CRA0201-CHAVE-3 WITH DUPLICATES FILE STATUS IS WT-ST-CRA0201.
O book de detalhamento do arquivo se chama CRF0201:
FD CRA0201. 01 CRA0201-REGISTRO. 03 CRA0201-CHAVE-1. 05 CRA0201-CD-CLIENTE PIC X(006). 03 CRA0201-NM-CLIENTE PIC X(045). 03 CRA0201-NM-LOGRADOURO PIC X(045). 03 CRA0201-NM-BAIRRO PIC X(020). 03 CRA0201-NM-CIDADE PIC X(020). 03 CRA0201-CD-ESTADO PIC X(002). 03 CRA0201-NR-TELEFONE-1 PIC X(020). 03 CRA0201-NR-TELEFONE-2 PIC X(020). 03 CRA0201-NM-EMAIL PIC X(030). 03 CRA0201-CHAVE-2. 05 CRA0201-NR-CNPJ PIC 9(014). 03 CRA0201-CHAVE-3. 05 CRA0201-CD-SEGMENTO PIC X(003).
Vamos começar justamente inserindo o book de declaração na ENVIRONMENT DIVISION:
*================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * VENDAS MENSAIS COPY FAS0500. * CADASTRO DE CLIENTES COPY CRS0201. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO SELECT SRT0500 ASSIGN TO "$DAT/SRT0500.DAT" ORGANIZATION IS LINE SEQUENTIAL. * RELATORIO DE VENDAS POR VENDEDOR SELECT FAL0501 ASSIGN TO "$DAT/FAL0501.TXT" ORGANIZATION IS LINE SEQUENTIAL.
E o book de detalhamento na DATA DIVISION:
*================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * VENDAS MENSAIS COPY FAF0500. * CADASTRO DE CLIENTES COPY CRF0201. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO SD SRT0500. 01 SRT0500-REGISTRO. 03 SRT0500-NR-VENDEDOR PIC 9(006). 03 FILLER PIC X(035). * RELATORIO DE VENDAS POR VENDEDOR FD FAL0501. 01 FAL0501-REGISTRO PIC X(080).
Como temos um novo arquivo no programa, precisamos criar uma variável na WORKING para o file status das operações que realizaremos sobre ele:
01 WT-FILE-STATUS.
03 WT-ST-FAA0500 PIC X(002).
03 WT-ST-CRA0201 PIC X(002).
Agora vamos alterar a linha detalhe e a linha que contém o título das colunas para inserir o nome do cliente e deslocar o número da fatura para a direita:
01 WR-CAB4. 03 FILLER PIC X(015) VALUE "VENDEDOR". 03 FILLER PIC X(013) VALUE "DATA". 03 FILLER PIC X(013) VALUE "CLIENTE". 03 FILLER PIC X(033) VALUE "CLIENTE". 03 FILLER PIC X(034) VALUE "FATURA". 03 FILLER PIC X(014) VALUE "FATURA". 03 FILLER PIC X(005) VALUE "VALOR". 01 WR-DET1. 03 WR-DET-NR-VENDEDOR PIC 9(006) VALUE ZEROS BLANK WHEN ZEROS. 03 FILLER PIC X(007) VALUE SPACES. 03 WR-DET-DT-VENDA PIC X(010) VALUE SPACES. 03 FILLER PIC X(005) VALUE SPACES. 03 WR-DET-CD-CLIENTE PIC X(006) VALUE SPACES. 03 WR-DET-NM-CLIENTE PIC X(030) VALUE SPACES. 03 FILLER PIC X(007) VALUE SPACES. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-NR-FATURA PIC 9(006) VALUE ZEROS. 03 FILLER PIC X(023) VALUE SPACES. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-VL-VENDA PIC ZZZ.ZZ9,99 VALUE ZEROS.
O arquivo CRA0201 precisa ser aberto no parágrafo inicial junto com os outros arquivos. Ele será aberto em modo INPUT pois só pretendemos ler os seus registros:
*----------------------------------------------------------------*
* CLASSIFICA O ARQUIVO DE ENTRADA POR NUMERO DO VENDEDOR. EM SE-
* GUIDA, ABRE OS ARQUIVOS E LE O PRIMEIRO REGISTRO DO ARQUIVO DE
* ENTRADA
*----------------------------------------------------------------*
1-INICIA.
SORT SRT0500
ON ASCENDING KEY SRT0500-NR-VENDEDOR
USING FAA0500
GIVING FAA0500
OPEN INPUT FAA0500
OPEN INPUT CRA0201
OPEN OUTPUT FAL0501
READ FAA0500.
Agora vamos procurar o parágrafo que imprime a linha detalhe e substituir a movimentação do código do cliente pela movimentação do nome do cliente. Mas antes disso vamos inserir o comando de leitura.
A leitura randômica de arquivos indexados também é realizada com o comando READ. A única diferença é que antes do comando precisamos mover o valor da chave que pretendemos ler.
No nosso caso, ao invés de mover o código do cliente para o relatório, vamos mover esse código para a chave primária do cadastro de clientes, fazer a leitura, verificar se a leitura foi bem sucedida e mover o nome do cliente para o relatório:
*----------------------------------------------------------------* * IMPRIME TODAS AS VENDAS DO VENDEDOR *----------------------------------------------------------------* 21-IMPRIME-VENDAS. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF CALL WT-NM-EDT4 USING FAA0500-DT-VENDA WR-DET-DT-VENDA WT-RC-EDT4 MOVE FAA0500-CD-CLIENTE TO WR-DET-NM-CLIENTE MOVE FAA0500-CD-CLIENTE TO CRA0201-CD-CLIENTE READ CRA0201 IF WT-ST-CRA0201 = “00” MOVE CRA0201-NM-CLIENTE TO WR-DET-NM-CLIENTE ELSE MOVE “*** NAO ENCONTRADO ***” TO WR-DET-NM-CLIENTE END-IF MOVE FAA0500-NR-FATURA TO WR-DET-NR-FATURA MOVE FAA0500-VL-VENDA TO WR-DET-VL-VENDA WRITE FAL0501-REGISTRO FROM WR-DET1 ADD 1 TO WT-CT-LINHAS ADD FAA0500-VL-VENDA TO WT-AC-VENDAS MOVE ZEROS TO WR-DET-NR-VENDEDOR READ FAA0500.
Repare que depois da leitura estamos testando o file status da operação. Se o file status for qualquer coisa diferente de “00” nós mostraremos “*** NAO ENCONTRADO ***” no nome do cliente. Essa é uma forma “preguiçosa” de proteger o programa contra erros de leitura. O ideal seria testar o file status e…
- Mover o nome do cliente para o relatório quando file status fosse igual a “00”
- Mover “*** NAO ENCONTRADO ***” para o nome do cliente quando o file status fosse igual a “23” (registro não encontrado)
- Exibir uma mensagem de erro e interromper o programa quando o file status tivesse qualquer outro valor; erro não previsto que precisa ser investigado
Como aqui só queremos mostrar o uso de leitura em arquivos indexados, vamos ficar com a forma preguiçosa mesmo.
O comando READ para arquivos indexados possui um outro formato que poderíamos ter usado nesse parágrafo:
READ arquivo INVALID KEY Comando-1 Comando-2 Comando-3 NOT INVALID KEY Comando-4 Comando-5 Comando-6 END-READ
Os comandos do bloco INVALID KEY são executados sempre que a leitura é mal sucedida, equivalente ao file status diferente de “00”. Os comandos do bloco NOT INVALID KEY são executados com a leitura é realizada com sucesso, equivalente ao file status igual a “00”.
No nosso exemplo ficaria assim:
MOVE FAA0500-CD-CLIENTE TO CRA0201-CD-CLIENTE READ CRA0201 INVALID KEY MOVE “*** NAO ENCONTRADO *** TO WR-DET-NM-CLIENTE NOT INVALID KEY MOVE CRA0201-NM-CLIENTE TO WR-DET-NM-CLIENTE END-READ
O formato simples, com teste de file status, te dará mais liberdade. Se amanhã você quiser fazer um tratamento mais completo para diferentes valores de file status, a solução com IF ficará mais simples e clara.
Nossa última alteração será no parágrafo de término do programa, onde fecharemos o cadastro de clientes:
*----------------------------------------------------------------*
* FECHA ARQUIVOS
*----------------------------------------------------------------*
3-TERMINA.
CANCEL WT-NM-EDT4
CLOSE FAA0500 FAL0501 CRA0201.
Nosso programa completo ficou assim:
*================================================================* IDENTIFICATION DIVISION. *----------------------------------------------------------------* PROGRAM-ID. FAP0501. AUTHOR. PAULO ANDRE DIAS. DATE-WRITTEN. 14/01/2017. REMARKS. *----------------------------------------------------------------* * SISTEMA: FA - FATURAMENTO * JOB: 05 - RELATORIOS * PROGRAMA: 01 - VENDAS POR VENDEDOR * * OBJETIVO: LER O ARQUIVO DE VENDAS NO MES E GERAR RELATORIO * DE VENDAS POR VENDEDOR * * VERSOES: DATA DESCRICAO * ------ --------------------------------------- * XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *----------------------------------------------------------------* *================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * VENDAS MENSAIS COPY FAS0500. * CADASTRO DE CLIENTES COPY CRS0201. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO SELECT SRT0500 ASSIGN TO "$DAT/SRT0500.DAT" ORGANIZATION IS LINE SEQUENTIAL. * RELATORIO DE VENDAS POR VENDEDOR SELECT FAL0501 ASSIGN TO "$DAT/FAL0501.TXT" ORGANIZATION IS LINE SEQUENTIAL. *================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * VENDAS MENSAIS COPY FAF0500. * CADASTRO DE CLIENTES COPY CRF0201. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO SD SRT0500. 01 SRT0500-REGISTRO. 03 SRT0500-NR-VENDEDOR PIC 9(006). 03 FILLER PIC X(035). * RELATORIO DE VENDAS POR VENDEDOR FD FAL0501. 01 FAL0501-REGISTRO PIC X(080). *================================================================* WORKING-STORAGE SECTION. *----------------------------------------------------------------* 01 WC-CONSTANTES. 03 WC-LINHAS-POR-PAGINA PIC 9(002) VALUE 60. 01 WT-FILE-STATUS. 03 WT-ST-FAA0500 PIC X(002) VALUE SPACES. 03 WT-ST-CRA0201 PIC X(002) VALUE SPACES. 01 WT-CONTADORES. 03 WT-CT-PAGINA PIC 9(003) VALUE ZEROS. 03 WT-CT-LINHAS PIC 9(002) VALUE 99. 01 WT-ACUMULADORES. 03 WT-AC-VENDAS 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 WT-CAMPOS-CONTROLE. 03 WT-CTL-NR-VENDEDOR PIC 9(006) 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 "FATURAMENTO". 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(030) VALUE "FAL0501". 03 FILLER PIC X(036) VALUE "VENDAS POR VENDEDOR". 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(015) VALUE "VENDEDOR". 03 FILLER PIC X(013) VALUE "DATA". 03 FILLER PIC X(033) VALUE "CLIENTE". 03 FILLER PIC X(014) VALUE "FATURA". 03 FILLER PIC X(005) VALUE "VALOR". 01 WR-DET1. 03 WR-DET-NR-VENDEDOR PIC 9(006) VALUE ZEROS BLANK WHEN ZEROS. 03 FILLER PIC X(007) VALUE SPACES. 03 WR-DET-DT-VENDA PIC X(010) VALUE SPACES. 03 FILLER PIC X(005) VALUE SPACES. 03 WR-DET-NM-CLIENTE PIC X(030) VALUE SPACES. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-NR-FATURA PIC 9(006) VALUE ZEROS. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-VL-VENDA PIC ZZZ.ZZ9,99 VALUE ZEROS. 01 WR-ROD1. 03 FILLER PIC X(068) VALUE SPACES. 03 FILLER PIC X(012) VALUE ALL "-". 01 WR-ROD2. 03 FILLER PIC X(059) VALUE SPACES. 03 FILLER PIC X(009) VALUE "TOTAL: ". 03 WR-ROD-AC-VENDAS PIC Z.ZZZ.ZZ9,99 VALUE ZEROS. *================================================================* PROCEDURE DIVISION. *----------------------------------------------------------------* 0-PRINCIPAL. PERFORM 1-INICIA PERFORM 2-PROCESSA UNTIL WT-ST-FAA0500 NOT = "00" PERFORM 3-TERMINA STOP RUN. *----------------------------------------------------------------* * CLASSIFICA O ARQUIVO DE ENTRADA POR NUMERO DO VENDEDOR. EM SE- * GUIDA, ABRE OS ARQUIVOS E LE O PRIMEIRO REGISTRO DO ARQUIVO DE * ENTRADA *----------------------------------------------------------------* 1-INICIA. SORT SRT0500 ON ASCENDING KEY SRT0500-NR-VENDEDOR USING FAA0500 GIVING FAA0500 OPEN INPUT FAA0500 OPEN INPUT CRA0201 OPEN OUTPUT FAL0501 READ FAA0500. *----------------------------------------------------------------* * SALVA O CAMPO DE CONTROLE (NUMERO DO VENDEDOR) IMPRIME TODAS * AS SUAS VENDAS E IMPRIME SEU TOTAL DE VENDAS NO PERIODO *----------------------------------------------------------------* 2-PROCESSA. MOVE FAA0500-NR-VENDEDOR TO WT-CTL-NR-VENDEDOR MOVE FAA0500-NR-VENDEDOR TO WR-DET-NR-VENDEDOR MOVE ZEROS TO WT-AC-VENDAS PERFORM 21-IMPRIME-VENDAS UNTIL WT-ST-FAA0500 NOT = "00" OR FAA0500-NR-VENDEDOR NOT = WT-CTL-NR-VENDEDOR PERFORM 22-IMPRIME-TOTAL. *----------------------------------------------------------------* * FECHA ARQUIVOS *----------------------------------------------------------------* 3-TERMINA. CANCEL WT-NM-EDT4 CLOSE FAA0500 FAL0501 CRA0201. *----------------------------------------------------------------* * IMPRIME TODAS AS VENDAS DO VENDEDOR *----------------------------------------------------------------* 21-IMPRIME-VENDAS. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF CALL WT-NM-EDT4 USING FAA0500-DT-VENDA WR-DET-DT-VENDA WT-RC-EDT4 MOVE FAA0500-CD-CLIENTE TO CRA0201-CD-CLIENTE READ CRA0201 IF WT-ST-CRA0201 = "00" MOVE CRA0201-NM-CLIENTE TO WR-DET-NM-CLIENTE ELSE MOVE "*** NAO ENCONTRADO ***" TO WR-DET-NM-CLIENTE END-IF MOVE FAA0500-NR-FATURA TO WR-DET-NR-FATURA MOVE FAA0500-VL-VENDA TO WR-DET-VL-VENDA WRITE FAL0501-REGISTRO FROM WR-DET1 ADD 1 TO WT-CT-LINHAS ADD FAA0500-VL-VENDA TO WT-AC-VENDAS MOVE ZEROS TO WR-DET-NR-VENDEDOR READ FAA0500. *----------------------------------------------------------------* * IMPRIME TOTAL POR VENDEDOR *----------------------------------------------------------------* 22-IMPRIME-TOTAL. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF MOVE WT-AC-VENDAS TO WR-ROD-AC-VENDAS WRITE FAL0501-REGISTRO FROM WR-ROD1 WRITE FAL0501-REGISTRO FROM WR-ROD2 BEFORE 2 ADD 3 TO WT-CT-LINHAS. *----------------------------------------------------------------* * 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 FAL0501-REGISTRO FROM WR-CAB1 AFTER PAGE WRITE FAL0501-REGISTRO FROM WR-CAB2 WRITE FAL0501-REGISTRO FROM WR-CAB3 WRITE FAL0501-REGISTRO FROM WR-SEP1 WRITE FAL0501-REGISTRO FROM WR-CAB4 WRITE FAL0501-REGISTRO FROM WR-SEP1 MOVE 6 TO WT-CT-LINHAS.
E o relatório FAL0501.TXT, depois das alterações, ficou assim:
EMPRESA XYZ DATA: 20/01/17 FATURAMENTO HORA: 09:22:52 FAL0501 VENDAS POR VENDEDOR PAGINA: 1 -------------------------------------------------------------------------------- VENDEDOR DATA CLIENTE FATURA VALOR -------------------------------------------------------------------------------- 000001 11/05/2015 IMOBILIARIA DEDO DE DEUS 015001 21.250,00 11/05/2015 OLIVEIRA SANTOS CONTABILIDADE 015002 74.750,00 13/05/2015 PAPELARIA OURO PRETO 015005 25.790,00 ------------ TOTAL: 121.790,00 000002 11/05/2015 PROMOLIGHT EVENTOS 015003 116.414,00 12/05/2015 CONSTRUTORA JJR 015004 8.515,00 ------------ TOTAL: 124.929,00 000003 14/05/2015 ALTO DROGARIAS 015006 1.600,00 ------------ TOTAL: 1.600,00 000004 15/05/2015 FARID ELETROMOVEIS 015007 35.300,00 15/05/2015 POLICLININCA IMBUI 015009 7.280,00 15/05/2015 SUPERMERCADO ABC 015011 41.200,00 15/05/2015 CONFECCAO TERESOPOLIS 015012 8.920,00 ------------ TOTAL: 92.700,00 000005 15/05/2015 CONDOMINIO GREEN VILLAGE 015008 6.665,00 15/05/2015 TEREPOINT PECAS E AUTOPECAS 015010 12.458,00 16/05/2015 SUPERMERCADO ITAMARKET 015013 1.740,00 ------------ TOTAL: 20.863,00 000006 16/05/2015 CASA COLORIDA MOVEIS 015014 64.300,00 ------------ TOTAL: 64.300,00
Gravando, alterando e apagando registros
A possibilidade de acessar registros diretamente a partir de chaves ou índices faz do arquivo indexado a opção mais indicada quando precisamos gravar, alterar e/ou apagar registros randomicamente e não temos acesso a um banco de dados.
Para mostrar como realizar essas operações, vamos construir um programa simples que lê um arquivo sequencial e gera um arquivo indexado.
O arquivo de entrada é um arquivo sequencial linear, cuja declaração está disponível no book CRS0200 e que tem o seguinte conteúdo:
*----------------------------------------------------------------* * INTERFACE PARA ATUALIZACAO DO CADASTRO DE CLIENTES *----------------------------------------------------------------* SELECT CRA0200 ASSIGN TO "$DAT/CRA0200.DAT" ORGANIZATION IS LINE SEQUENTIAL FILE STATUS IS WT-ST-CRA0200.
Seu layout está detalhado no book CRF0200. O primeiro campo, TP-OPERACAO, nos diz o que será feito com o registro: “I” significa que ele deve ser incluído, “A” significa que ele deve ser alterado e “E” significa que ele deve ser excluído.
*----------------------------------------------------------------*
* INTERFACE PARA ATUALIZACAO DO CADASTRO DE CLIENTES
*----------------------------------------------------------------*
FD CRA0200.
01 CRA0200-REGISTRO.
03 CRA0200-TP-OPERACAO PIC X(001).
03 CRA0200-CD-CLIENTE PIC X(006).
03 CRA0200-NM-CLIENTE PIC X(045).
03 CRA0200-NM-LOGRADOURO PIC X(045).
03 CRA0200-NM-BAIRRO PIC X(020).
03 CRA0200-NM-CIDADE PIC X(020).
03 CRA0200-CD-ESTADO PIC X(002).
03 CRA0200-CD-CEP PIC X(008).
03 CRA0200-NR-TELEFONE-1 PIC X(020).
03 CRA0200-NR-TELEFONE-2 PIC X(020).
03 CRA0200-NM-EMAIL PIC X(030).
03 CRA0200-NR-CNPJ PIC 9(014).
03 CRA0200-CD-SEGMENTO PIC X(003).
O arquivo indexado que iremos atualizar é o mesmo que usamos no exemplo anterior. Ele será declarado com o book CRS0201 e detalhado com o book CRF0201, que vimos em nosso último programa.
A gravação, alteração e exclusão de registros acontecerá em três parágrafos diferentes, como mostrado no diagrama abaixo:
O parágrafo 2-PROCESSA será executado até o fim do arquivo CRA0200.
Se o campo CRA0200-TP-PROCESSO estiver preenchido com “I” o programa vai executar o parágrafo 21-INCLUI-REGISTRO para inserir um novo cliente no arquivo CRA0201.
Se o campo CRA0200-TP-PROCESSO estiver preenchido com “A” o programa vai executar o parágrafo 22-ALTERA-REGISTRO para substituir todos os campos do cliente pelo conteúdo que estiver no arquivo sequencial.
Se o campo CRA0200-TP-PROCESSO estiver preenchido com “E” o programa vai executar o parágrafo 23-EXCLUI-REGISTRO para excluir o cliente do arquivo indexado.
Para simplificar o exemplo (e focar apenas nas operações com arquivos indexados) vamos assumir que não existe possibilidade do arquivo CRA0200 ter o campo TP-PROCESSO preenchido com um valor diferente de “I”, “A” ou “E”.
Nesse programa usaremos bastante os testes com file status para verificar se todas as operações foram bem sucedidas. Se detectarmos qualquer problema nas operações de leitura, gravação, atualização ou exclusão mostraremos uma mensagem na tela do usuário indicando a operação, o tipo de erro (valor do file status) e o código do cliente que provocou o problema. Se a operação for bem sucedida (file status “00”) diremos apenas que o registro foi incluído ou alterado ou excluído.
No final do programa, exibiremos a quantidade de registros lidos, gravados, alterados e excluídos.
Da IDENTIFICATION até o FILE SECTION não há nenhuma novidade:
*================================================================* IDENTIFICATION DIVISION. *----------------------------------------------------------------* PROGRAM-ID. CRP0301. AUTHOR. PAULO ANDRE DIAS. DATE-WRITTEN. 20/01/2017. REMARKS. *----------------------------------------------------------------* * SISTEMA: CR - CONTAS A RECEBER * JOB: 03 - ATUALIZACAO CADASTRAL * PROGRAMA: 01 - ATUALIZA CADASTRO DE CLIENTES * * OBJETIVO: LER ARQUIVO SEQUENCIAL E INCLUIR OU ALTERAR RE- * GISTROS NO CADASTRO DE CLIENTES * * VERSOES: DATA DESCRICAO * ------ --------------------------------------- * XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *----------------------------------------------------------------* *================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * INTERFACE DE CLIENTES PARA ATUALIZACAO COPY CRS0200. * CADASTRO DE CLIENTES COPY CRS0201. *================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * INTERFACE DE CLIENTES PARA ATUALIZACAO COPY CRF0200. * CADASTRO DE CLIENTES COPY CRF0201. Na WORKING declararemos as variáveis para file status e os contadores que exibiremos no final do programa: *================================================================* WORKING-STORAGE SECTION. *----------------------------------------------------------------* 01 WT-FILE-STATUS. 03 WT-ST-CRA0200 PIC X(002) VALUE SPACES. 03 WT-ST-CRA0201 PIC X(002) VALUE SPACES. 01 WT-CONTADORES. 03 WT-CT-LIDOS PIC 9(006) VALUE ZEROS. 03 WT-CT-GRAVADOS PIC 9(006) VALUE ZEROS. 03 WT-CT-ALTERADOS PIC 9(006) VALUE ZEROS. 03 WT-CT-EXCLUIDOS PIC 9(006) VALUE ZEROS.
Como previsto na especificação, o parágrafo 2-PROCESSA será executado até que não haja mais registros no arquivo de entrada:
*================================================================*
PROCEDURE DIVISION.
*----------------------------------------------------------------*
0-PRINCIPAL.
PERFORM 1-INICIA
PERFORM 2-PROCESSA
UNTIL WT-ST-CRA0200 NOT = "00"
PERFORM 3-TERMINA
STOP RUN.
Sabemos que os registros poderão ser gravados, alterados ou excluídos, mas não queremos apagar o conteúdo que já existir no arquivo. Por isso não podemos abrir o arquivo em modo OUTPUT. Ao invés disso, usaremos o modo I-O, que faz com que o arquivo fique disponível para operações de entrada e saída. A abertura do arquivo indexado ficará no parágrafo 1-INICIA, seguindo o padrão de codificação que estamos adotando nesse livro:
*----------------------------------------------------------------*
* ABRE ARQUIVOS E LE PRIMEIRO REGISTRO DO ARQUIVO DE ENTRADA
*----------------------------------------------------------------*
1-INICIA.
OPEN INPUT CRA0200
OPEN I-O CRA0201
READ CRA0200.
O parágrafo 2-PROCESSA avalia o valor do campo TP-PROCESSO e executa os parágrafos correspondentes para inclusão, alteração ou exclusão de registros. Para isso vamos usar o comando EVALUATE apenas por uma questão de estética, mas poderíamos também codificar um ninho com três IFs para obter o mesmo resultado. Já vimos a sintaxe do comando EVALUATE quando começamos a falar sobre programação estruturada.
O último comando do parágrafo é a leitura do próximo registro do arquivo sequencial de entrada:
*----------------------------------------------------------------* * SE O CLIENTE JA' EXISTIR NO CADASTRO O REGISTRO SERA' ATUALIZA- * DO. SE NAO EXISTIR O REGISTRO SERA' INSERIDO *----------------------------------------------------------------* 2-PROCESSA. ADD 1 TO WT-CT-LIDOS EVALUATE CRA0200-TP-OPERACAO WHEN "I" PERFORM 21-INCLUI-REGISTRO WHEN "A" PERFORM 22-ALTERA-REGISTRO WHEN "E" PERFORM 23-EXCLUI-REGISTRO END-EVALUATE READ CRA0200.
No parágrafo 21-INCLUI-REGISTRO vamos mover todos os campos do arquivo de entrada para o arquivo de saída e em seguida gravar o registro no arquivo indexado usando o comando WRITE. O formato que vamos usar para o WRITE é o mesmo que usamos anteriormente para arquivos sequenciais:
*----------------------------------------------------------------* * INCLUI REGISTRO NO CADASTRO DE CLIENTES *----------------------------------------------------------------* 21-INCLUI-REGISTRO. MOVE CRA0200-CD-CLIENTE TO CRA0201-CD-CLIENTE MOVE CRA0200-NM-CLIENTE TO CRA0201-NM-CLIENTE MOVE CRA0200-NM-LOGRADOURO TO CRA0201-NM-LOGRADOURO MOVE CRA0200-NM-BAIRRO TO CRA0201-NM-BAIRRO MOVE CRA0200-NM-CIDADE TO CRA0201-NM-CIDADE MOVE CRA0200-CD-ESTADO TO CRA0201-CD-ESTADO MOVE CRA0200-CD-CEP TO CRA0201-CD-CEP MOVE CRA0200-NR-TELEFONE-1 TO CRA0201-NR-TELEFONE-1 MOVE CRA0200-NR-TELEFONE-2 TO CRA0201-NR-TELEFONE-2 MOVE CRA0200-NM-EMAIL TO CRA0201-NM-EMAIL MOVE CRA0200-NR-CNPJ TO CRA0201-NR-CNPJ MOVE CRA0200-CD-SEGMENTO TO CRA0201-CD-SEGMENTO WRITE CRA0201-REGISTRO
O comando WRITE para arquivos indexados possui um outro formato, um pouco mais complexo:
WRITE registro INVALID KEY Comando-1 Comando-2 Comando-3 NOT INVALID KEY Comando-4 Comando-5 Comando-6 END-WRITE
No exemplo acima, o bloco de comandos 1 a 3 só seria executado quando a tentativa de gravação resultasse num file status diferente de “00”. O bloco dos comandos 4 a 6 seria executado quando file status fosse igual a “00”. Apenas para manter uma coerência com os outros programas, aqui também preferimos testar o file status:
*----------------------------------------------------------------* * INCLUI REGISTRO NO CADASTRO DE CLIENTES *----------------------------------------------------------------* 21-INCLUI-REGISTRO. MOVE CRA0200-CD-CLIENTE TO CRA0201-CD-CLIENTE MOVE CRA0200-NM-CLIENTE TO CRA0201-NM-CLIENTE MOVE CRA0200-NM-LOGRADOURO TO CRA0201-NM-LOGRADOURO MOVE CRA0200-NM-BAIRRO TO CRA0201-NM-BAIRRO MOVE CRA0200-NM-CIDADE TO CRA0201-NM-CIDADE MOVE CRA0200-CD-ESTADO TO CRA0201-CD-ESTADO MOVE CRA0200-CD-CEP TO CRA0201-CD-CEP MOVE CRA0200-NR-TELEFONE-1 TO CRA0201-NR-TELEFONE-1 MOVE CRA0200-NR-TELEFONE-2 TO CRA0201-NR-TELEFONE-2 MOVE CRA0200-NM-EMAIL TO CRA0201-NM-EMAIL MOVE CRA0200-NR-CNPJ TO CRA0201-NR-CNPJ MOVE CRA0200-CD-SEGMENTO TO CRA0201-CD-SEGMENTO WRITE CRA0201-REGISTRO IF WT-ST-CRA0201 = "00" ADD 1 TO WT-CT-GRAVADOS DISPLAY "CLIENTE " CRA0200-CD-CLIENTE " INCLUIDO" ELSE DISPLAY "*** ERRO " WT-ST-CRA0201 " NA INCLUSAO DO CLIENTE " CRA0200-CD-CLIENTE END-IF.
Se a gravação for bem sucedida, exibiremos a seguinte mensagem na tela do usuário:
CLIENTE 999999 INCLUIDO
Se houver qualquer problema na gravação exibiremos a mensagem abaixo:
*** ERRO XX NA INCLUSAO DO CLIENTE 999999
XX será substituído pelo valor do file status. 999999 será substituído pelo código do cliente envolvido na operação.
A alteração (ou regravação) de registros em arquivos indexados é executada pelo comando REWRITE. Quando um arquivo indexado é acesso em modo sequencial (ACCESS MODE IS SEQUENTIAL) o REWRITE é executado sobre o último registro lido. Como estamos acessando o registro em modo dinâmico (ACCESS MODE IS DYNAMIC) essa leitura prévia não é necessária.
Só precisamos, portanto, mover todos os campos do arquivo de entrada para o arquivo de saída e executar o comando de regravação. Depois do comando testaremos o file status da operação de forma semelhante ao que fizemos no parágrafo anterior.
*----------------------------------------------------------------* * ALTERA REGISTRO NO CADASTRO DE CLIENTES *----------------------------------------------------------------* 22-ALTERA-REGISTRO. MOVE CRA0200-CD-CLIENTE TO CRA0201-CD-CLIENTE MOVE CRA0200-NM-CLIENTE TO CRA0201-NM-CLIENTE MOVE CRA0200-NM-LOGRADOURO TO CRA0201-NM-LOGRADOURO MOVE CRA0200-NM-BAIRRO TO CRA0201-NM-BAIRRO MOVE CRA0200-NM-CIDADE TO CRA0201-NM-CIDADE MOVE CRA0200-CD-ESTADO TO CRA0201-CD-ESTADO MOVE CRA0200-CD-CEP TO CRA0201-CD-CEP MOVE CRA0200-NR-TELEFONE-1 TO CRA0201-NR-TELEFONE-1 MOVE CRA0200-NR-TELEFONE-2 TO CRA0201-NR-TELEFONE-2 MOVE CRA0200-NM-EMAIL TO CRA0201-NM-EMAIL MOVE CRA0200-NR-CNPJ TO CRA0201-NR-CNPJ MOVE CRA0200-CD-SEGMENTO TO CRA0201-CD-SEGMENTO REWRITE CRA0201-REGISTRO IF WT-ST-CRA0201 = "00" ADD 1 TO WT-CT-ALTERADOS DISPLAY "CLIENTE " CRA0200-CD-CLIENTE " ALTERADO" ELSE DISPLAY "*** ERRO " WT-ST-CRA0201 " NA ALTERACAO DO CLIENTE " CRA0200-CD-CLIENTE END-IF.
O comando REWRITE também possui um formato com cláusulas INVALID KEY e NOT INVALID KEY, semelhante aos comandos READ e WRITE:
REWRITE registro INVALID KEY Comando-1 Comando-2 Comando-3 NOT INVALID KEY Comando-4 Comando-5 Comando-6 END-REWRITE
A exclusão de registros em arquivos indexados é executada pelo comando DELETE. Ao contrário dos comandos WRITE e REWRITE, para o comando DELETE precisamos indicar o nome do arquivo cujo registro queremos deletar, e não o nome do registro.
Quando um arquivo indexado é acesso em modo sequencial (ACCESS MODE IS SEQUENTIAL) o comando DELETE é executado sobre o último registro lido. Como estamos acessando o arquivo em modo dinâmico (ACCESS MODE IS DYNAMIC) essa leitura prévia não é necessária. Só precisamos mover o valor da chave que pretendemos excluir e executar o comando:
*----------------------------------------------------------------* * EXCLUI REGISTRO DO CADASTRO DE CLIENTES *----------------------------------------------------------------* 23-EXCLUI-REGISTRO. MOVE CRA0200-CD-CLIENTE TO CRA0201-CD-CLIENTE DELETE CRA0201 IF WT-ST-CRA0201 = "00" ADD 1 TO WT-CT-EXCLUIDOS DISPLAY "CLIENTE " CRA0200-CD-CLIENTE " EXCLUIDO" ELSE DISPLAY "*** ERRO " WT-ST-CRA0201 " NA EXCLUSAO DO CLIENTE " CRA0200-CD-CLIENTE END-IF.
O comando DELETE também possui um formato com INVALID e NOT INVALID KEY:
DELETE arquivo INVALID KEY Bloco-de-comandos-1 NOT INVALID KEY Bloco-de-comandos-2 END-DELETE
O comandos do bloco 1 serão executados se o programa não encontrar o registro cuja chave foi previamente informada, mesma situação do file status diferente de “00”. Os comandos do bloco 2 serão executados se a operação de exclusão for bem sucedida, equivalente ao file status “00”.
O parágrafo de término vai fechar os arquivos e exibir a quantidade de registros lidos, gravados, alterados e excluídos:
*----------------------------------------------------------------* * FECHA ARQUIVOS *----------------------------------------------------------------* 3-TERMINA. CLOSE CRA0200 CRA0201 DISPLAY "LIDOS=" WT-CT-LIDOS " GRAVADOS=" WT-CT-GRAVADOS " ALTERADOS=" WT-CT-ALTERADOS " EXCLUIDOS=" WT-CT-EXCLUIDOS.
O tratamento do file status que fizemos em todos os parágrafo nos dá muita flexibilidade na execução desse programa. Imagine, por exemplo, que o arquivo CRA0200 tenha os seguintes registros:
I000515FARID ELETROMOVEIS PRACA DOUTOR GUILHERME 105 CENTRO ITABIRITO MG35450000(31) 3561-1588 33184516000192VAR I000789POLICLININCA IMBUI AV OLIVEIRA BOTELHO 1340 SL 801 ALTO TERESOPOLIS RJ25600200(21) 2642-1244 JOAO@IMBUI.COM.BR 00000000000000SRV I000901SUPERMERCADO ABC AV FELICIANO SODRE 340 VARZEA TERESOPOLIS RJ25540150(21) 2643-3344 (21) 2643-3345 03352717000121VAR
A primeira posição de cada registro (campo TP-PROCESSAMENTO) indica que queremos incluir os três registros em nosso arquivo indexado. Quando executarmos o programa pela primeira vez ele exibirá as seguintes informações na tela:
CLIENTE 000515 INCLUIDO CLIENTE 000789 INCLUIDO CLIENTE 000901 INCLUIDO LIDOS=000003 GRAVADOS=000003 ALTERADOS=000000 EXCLUIDOS=000000
Mas se tentarmos executar o programa de novo, com o mesmo arquivo de entrada, ele mostrará:
*** ERRO 22 NA INCLUSAO DO CLIENTE 000515 *** ERRO 22 NA INCLUSAO DO CLIENTE 000789 *** ERRO 22 NA INCLUSAO DO CLIENTE 000901 LIDOS=000003 GRAVADOS=000000 ALTERADOS=000000 EXCLUIDOS=000000
Na segunda execução do programa os registros com chaves 515, 789 e 901 já existiam, e por isso o comando WRITE resultou num file status “22” (registro duplicado).
Esse tipo de programa é muito utilizado para atualização batch de arquivos indexados. Seu código completo ficou assim:
*================================================================* IDENTIFICATION DIVISION. *----------------------------------------------------------------* PROGRAM-ID. CRP0301. AUTHOR. PAULO ANDRE DIAS. DATE-WRITTEN. 20/01/2017. REMARKS. *----------------------------------------------------------------* * SISTEMA: CR - CONTAS A RECEBER * JOB: 03 - ATUALIZACAO CADASTRAL * PROGRAMA: 01 - ATUALIZA CADASTRO DE CLIENTES * * OBJETIVO: LER ARQUIVO SEQUENCIAL E INCLUIR OU ALTERAR RE- * GISTROS NO CADASTRO DE CLIENTES * * VERSOES: DATA DESCRICAO * ------ --------------------------------------- * XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *----------------------------------------------------------------* *================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * INTERFACE DE CLIENTES PARA ATUALIZACAO COPY CRS0200. * CADASTRO DE CLIENTES COPY CRS0201. *================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * INTERFACE DE CLIENTES PARA ATUALIZACAO COPY CRF0200. * CADASTRO DE CLIENTES COPY CRF0201. *================================================================* WORKING-STORAGE SECTION. *----------------------------------------------------------------* 01 WT-FILE-STATUS. 03 WT-ST-CRA0200 PIC X(002) VALUE SPACES. 03 WT-ST-CRA0201 PIC X(002) VALUE SPACES. 01 WT-CONTADORES. 03 WT-CT-LIDOS PIC 9(006) VALUE ZEROS. 03 WT-CT-GRAVADOS PIC 9(006) VALUE ZEROS. 03 WT-CT-ALTERADOS PIC 9(006) VALUE ZEROS. 03 WT-CT-EXCLUIDOS PIC 9(006) VALUE ZEROS. *================================================================* PROCEDURE DIVISION. *----------------------------------------------------------------* 0-PRINCIPAL. PERFORM 1-INICIA PERFORM 2-PROCESSA UNTIL WT-ST-CRA0200 NOT = "00" PERFORM 3-TERMINA STOP RUN. *----------------------------------------------------------------* * ABRE ARQUIVOS E LE PRIMEIRO REGISTRO DO ARQUIVO DE ENTRADA *----------------------------------------------------------------* 1-INICIA. OPEN INPUT CRA0200 OPEN I-O CRA0201 READ CRA0200. *----------------------------------------------------------------* * SE O CLIENTE JA' EXISTIR NO CADASTRO O REGISTRO SERA' ATUALIZA- * DO. SE NAO EXISTIR O REGISTRO SERA' INSERIDO *----------------------------------------------------------------* 2-PROCESSA. ADD 1 TO WT-CT-LIDOS EVALUATE CRA0200-TP-OPERACAO WHEN "I" PERFORM 21-INCLUI-REGISTRO WHEN "A" PERFORM 22-ALTERA-REGISTRO WHEN "E" PERFORM 23-EXCLUI-REGISTRO END-EVALUATE READ CRA0200. *----------------------------------------------------------------* * FECHA ARQUIVOS *----------------------------------------------------------------* 3-TERMINA. CLOSE CRA0200 CRA0201 DISPLAY "LIDOS=" WT-CT-LIDOS " GRAVADOS=" WT-CT-GRAVADOS " ALTERADOS=" WT-CT-ALTERADOS " EXCLUIDOS=" WT-CT-EXCLUIDOS. *----------------------------------------------------------------* * INCLUI REGISTRO NO CADASTRO DE CLIENTES *----------------------------------------------------------------* 21-INCLUI-REGISTRO. MOVE CRA0200-CD-CLIENTE TO CRA0201-CD-CLIENTE MOVE CRA0200-NM-CLIENTE TO CRA0201-NM-CLIENTE MOVE CRA0200-NM-LOGRADOURO TO CRA0201-NM-LOGRADOURO MOVE CRA0200-NM-BAIRRO TO CRA0201-NM-BAIRRO MOVE CRA0200-NM-CIDADE TO CRA0201-NM-CIDADE MOVE CRA0200-CD-ESTADO TO CRA0201-CD-ESTADO MOVE CRA0200-CD-CEP TO CRA0201-CD-CEP MOVE CRA0200-NR-TELEFONE-1 TO CRA0201-NR-TELEFONE-1 MOVE CRA0200-NR-TELEFONE-2 TO CRA0201-NR-TELEFONE-2 MOVE CRA0200-NM-EMAIL TO CRA0201-NM-EMAIL MOVE CRA0200-NR-CNPJ TO CRA0201-NR-CNPJ MOVE CRA0200-CD-SEGMENTO TO CRA0201-CD-SEGMENTO WRITE CRA0201-REGISTRO IF WT-ST-CRA0201 = "00" ADD 1 TO WT-CT-GRAVADOS DISPLAY "CLIENTE " CRA0200-CD-CLIENTE " INCLUIDO" ELSE DISPLAY "*** ERRO " WT-ST-CRA0201 " NA INCLUSAO DO CLIENTE " CRA0200-CD-CLIENTE END-IF. *----------------------------------------------------------------* * ALTERA REGISTRO NO CADASTRO DE CLIENTES *----------------------------------------------------------------* 22-ALTERA-REGISTRO. MOVE CRA0200-CD-CLIENTE TO CRA0201-CD-CLIENTE MOVE CRA0200-NM-CLIENTE TO CRA0201-NM-CLIENTE MOVE CRA0200-NM-LOGRADOURO TO CRA0201-NM-LOGRADOURO MOVE CRA0200-NM-BAIRRO TO CRA0201-NM-BAIRRO MOVE CRA0200-NM-CIDADE TO CRA0201-NM-CIDADE MOVE CRA0200-CD-ESTADO TO CRA0201-CD-ESTADO MOVE CRA0200-CD-CEP TO CRA0201-CD-CEP MOVE CRA0200-NR-TELEFONE-1 TO CRA0201-NR-TELEFONE-1 MOVE CRA0200-NR-TELEFONE-2 TO CRA0201-NR-TELEFONE-2 MOVE CRA0200-NM-EMAIL TO CRA0201-NM-EMAIL MOVE CRA0200-NR-CNPJ TO CRA0201-NR-CNPJ MOVE CRA0200-CD-SEGMENTO TO CRA0201-CD-SEGMENTO REWRITE CRA0201-REGISTRO IF WT-ST-CRA0201 = "00" ADD 1 TO WT-CT-GRAVADOS DISPLAY "CLIENTE " CRA0200-CD-CLIENTE " ALTERADO" ELSE DISPLAY "*** ERRO " WT-ST-CRA0201 " NA ALTERACAO DO CLIENTE " CRA0200-CD-CLIENTE END-IF. *----------------------------------------------------------------* * EXCLUI REGISTRO DO CADASTRO DE CLIENTES *----------------------------------------------------------------* 23-EXCLUI-REGISTRO. MOVE CRA0200-CD-CLIENTE TO CRA0201-CD-CLIENTE DELETE CRA0201 IF WT-ST-CRA0201 = "00" ADD 1 TO WT-CT-EXCLUIDOS DISPLAY "CLIENTE " CRA0200-CD-CLIENTE " EXCLUIDO" ELSE DISPLAY "*** ERRO " WT-ST-CRA0201 " NA EXCLUSAO DO CLIENTE " CRA0200-CD-CLIENTE END-IF.
Lendo arquivos indexados em modo sequencial
Arquivos indexados também pode ser acessados sequencialmente, registro a registro, de forma semelhante às leituras que fizemos anteriormente com arquivos sequenciais ou sequenciais lineares.
Umas das maneiras de se fazer isso é estabelecer o modo de acesso sequencial na declaração do arquivo:
SELECT CRA0201 ASSIGN TO “$DAT/CRA0201”
ORGANIZATION IS INDEXED
ACCESS MODE IS SEQUENTIAL
RECORD KEY IS CRA0201-CD-CLIENTE
ALTERNATE RECORD KEY IS CRA0201-CD-SEGMENTO WITH DUPLICATES
ALTERNATE RECORD KEY IS CRA0201-NR-CNPJ WITH DUPLICATES
FILE STATUS IS WT-ST-CRA0201.
A partir daí, o arquivo é aberto na PROCEDURE (em modo INPUT ou I-O) e lido com o comando:
READ arquivo AT END Bloco-de-comandos-1 NOT AT END Bloco-de-comandos-2 END-READ
Com ACCESS MODE IS SEQUENTIAL o comando READ vai ler sempre o próximo registro do arquivo, na ordem de sua chave primária, do valor mais baixo para o mais alto.
Mas muitas vezes precisamos ler sequencialmente apenas um subconjunto de registros do arquivo, normalmente posicionando a primeira leitura em algum valor específico da chave primária ou de uma das chaves alternativas. Em COBOL isso é feito com o comando START:
START arquivo KEY condição nome-da-chave
A condição após a cláusula KEY é que define em que posição do arquivo a próxima leitura acontecerá. Por exemplo, o comando abaixo estabelece que a próxima leitura do arquivo buscará o primeiro registro cujo campo CD-SEGMENTO seja igual a “VAR”.
MOVE “VAR” TO CRA0201-CD-SEGMENTO START CRA0201 KEY EQUAL TO CRA0201-CHAVE-3
A cláusula KEY permite outras opções condicionais:
- KEY NOT EQUAL TO (ou KEY NOT =): Posiciona a próxima leitura no primeiro registro cuja chave é diferente do valor informado
- KEY GREATER THAN (ou KEY >): Posiciona a próxima leitura no primeiro registro cuja chave é maior do que o valor informado
- KEY NOT LESS THAN (ou KEY NOT <): Posiciona a próxima leitura no primeiro registro cuja chave é maior ou igual ao valor informado
- KEY GREATER THAN OR EQUAL TO (KEY >=): Igual à opção anterior
Vale reforçar que o comando START não lê o registro; ele apenas posiciona a próxima leitura sequencial num registro que atenda à condição definida. A leitura acontece no READ subsequente:
MOVE “VAR” TO CRA0201-CD-SEGMENTO
START CRA0201 KEY EQUAL TO CRA0201-CHAVE-3
READ CRA0201 NEXT
A opção NEXT pode ser omitida do comando READ se o arquivo for acessado em modo sequencial, mas deve ser incluída se o ACCESS MODE for RANDOM ou DYNAMIC.
Vamos ver a aplicação desses comandos num programa completo. Vamos aproveitar para rever também alguns recursos que apresentamos em capítulos anteriores, como o uso de tabelas internas.
O programa vai receber um código de segmento da linha de comando e gerar um relatório com todos os clientes do arquivo CRA0201 que pertencem a esse segmento. Já vimos esse arquivo em exemplos anterior. Seu book de declaração se chama CRS0201 e tem o seguinte conteúdo:
*----------------------------------------------------------------* * CADASTRO DE CLIENTES *----------------------------------------------------------------* SELECT CRA0201 ASSIGN TO "$DAT/CRA0201.DAT" ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS CRA0201-CHAVE-1 ALTERNATE RECORD KEY IS CRA0201-CHAVE-2 WITH DUPLICATES ALTERNATE RECORD KEY IS CRA0201-CHAVE-3 WITH DUPLICATES FILE STATUS IS WT-ST-CRA0201.
Seu book de detalhamento se chama CRF0201:
*----------------------------------------------------------------* * CADASTRO DE CLIENTES *----------------------------------------------------------------* FD CRA0201. 01 CRA0201-REGISTRO. 03 CRA0201-CHAVE-1. 05 CRA0201-CD-CLIENTE PIC X(006). 03 CRA0201-NM-CLIENTE PIC X(045). 03 CRA0201-NM-LOGRADOURO PIC X(045). 03 CRA0201-NM-BAIRRO PIC X(020). 03 CRA0201-NM-CIDADE PIC X(020). 03 CRA0201-CD-ESTADO PIC X(002). 03 CRA0201-CD-CEP PIC X(008). 03 CRA0201-NR-TELEFONE-1 PIC X(020). 03 CRA0201-NR-TELEFONE-2 PIC X(020). 03 CRA0201-NM-EMAIL PIC X(030). 03 CRA0201-CHAVE-2. 05 CRA0201-NR-CNPJ PIC 9(014). 03 CRA0201-CHAVE-3. 05 CRA0201-CD-SEGMENTO PIC X(003).
O relatório mostrará apenas o código e o nome do cliente. O segmento informado na linha de comando aparecerá numa das linhas do cabeçalho. Haverá um rodapé mostrando a quantidade de clientes listados:
EMPRESA XYZ DATA: 99/99/99 FOLHA PAGTO HORA: 99:99:99 CRL0501 CLIENTES POR SEGMENTO PAGINA: ZZ9 -------------------------------------------------------------------------------- SEGMENTO: XXXXXXXXXX -------------------------------------------------------------------------------- CODIGO CLIENTE -------------------------------------------------------------------------------- XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -------------------------------------------------------------------------------- CLIENTES LISTADOS: ZZZ.ZZ9 --------------------------------------------------------------------------------
O usuário informará o código do segmento cujos clientes deseja listar na linha de comando. Esse código é um campo alfanumérico de três posições e é uma das chaves alternativas do arquivo. Ele pode assumir os seguintes valores:
Usaremos uma tabela interna para obter o nome do segmento a partir do código informado via argumento. Esse nome de segmento é que aparecerá no cabeçalho do relatório.
O programa terá apenas quatro parágrafos, além do parágrafo principal. O diagrama estruturado do programa será assim:
No parágrafo 1-INICIA colocaremos o comando START para posicionar o cadastro de clientes no primeiro registro cujo código de segmento seja igual ao código que foi informado como argumento na linha de comando.
O parágrafo 2-PROCESSA será executado até que todos os registros do cadastro de clientes tenham sido lidos OU que o código de segmento do cliente lido seja diferente do código informado via argumento. Nesse parágrafo faremos a impressão da linha de detalhe e chamaremos o parágrafo de impressão de cabeçalho quando a quantidade de linhas impressas for maior que o limite de linhas por página.
No parágrafo 3-TERMINA colocaremos a impressão do rodapé com a quantidade de clientes listados. Também chamaremos o parágrafo de impressão de cabeçalho se a quantidade de linhas impressas for maior que o limite de linhas por página.
No parágrafo X1-IMPRIME-CABECALHO usaremos o comando SEARCH para obter o nome de segmento associado ao código que foi informado via argumento. Já vimos a sintaxe desse comando no capítulo que falava sobre tabelas internas. O campo encontrado na tabela interna será movido para o cabeçalho.
Começamos construindo a IDENTIFICATION DIVISION de acordo com o padrão que utilizamos até agora:
*================================================================* IDENTIFICATION DIVISION. *----------------------------------------------------------------* PROGRAM-ID. CRP0501. AUTHOR. PAULO ANDRE DIAS. DATE-WRITTEN. 16/01/2017. REMARKS. *----------------------------------------------------------------* * SISTEMA: CR - CONTAS A RECEBER * JOB: 05 - RELATORIOS * PROGRAMA: 01 - RELACAO DE CLIENTES POR SEGMENTO * * OBJETIVO: LISTAR CODIGO E NOME DE TODOS OS CLIENTES CUJO * SEGMENTO TENHA SIDO INFORMADO VIA ARGUMENTO * * VERSOES: DATA DESCRICAO * ------ --------------------------------------- * XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *----------------------------------------------------------------*
Declaramos o arquivo de entrada e o relatório na ENVIRONMENT e na DATA DIVISION, sem nenhuma novidade:
*================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * SALARIOS COPY CRS0201. * RELACAO DE CLIENTES POR SEGMENTO SELECT CRL0501 ASSIGN TO "$DAT/CRL0501.TXT" ORGANIZATION IS LINE SEQUENTIAL. *================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * SALARIOS COPY CRF0201. * RELACAO DE CLIENTES POR SEGMENTO FD CRL0501. 01 CRL0501-REGISTRO PIC X(080).
Na WORKING, começamos com a constante que define o limite de linhas por página, com o file status do arquivo de entrada e com os contadores de páginas (que exibiremos no cabeçalho) e a quantidade de linhas impressas, que usaremos para decidir quando é a hora de imprimir um novo cabeçalho:
*================================================================* WORKING-STORAGE SECTION. *----------------------------------------------------------------* 01 WC-CONSTANTES. 03 WC-LINHAS-POR-PAGINA PIC 9(002) VALUE 60. 01 WT-FILE-STATUS. 03 WT-ST-CRA0201 PIC X(002) VALUE SPACES. 01 WT-CONTADORES. 03 WT-CT-PAGINA PIC 9(003) VALUE ZEROS. 03 WT-CT-LINHAS PIC 9(002) VALUE 99.
Nosso programa mostrará no rodapé do relatório a quantidade de clientes listados. Para isso precisamos criar um contador específico:
*================================================================*
WORKING-STORAGE SECTION.
*----------------------------------------------------------------*
01 WC-CONSTANTES.
03 WC-LINHAS-POR-PAGINA PIC 9(002) VALUE 60.
01 WT-FILE-STATUS.
03 WT-ST-CRA0201 PIC X(002) VALUE SPACES.
01 WT-CONTADORES.
03 WT-CT-PAGINA PIC 9(003) VALUE ZEROS.
03 WT-CT-LINHAS PIC 9(002) VALUE 99.
03 WT-CT-CLIENTES PIC 9(006) VALUE ZEROS.
Também precisamos criar variáveis auxiliares para formatação da data e da hora do sistema que usaremos no cabeçalho dos relatórios…
01 WT-AUXILIARES. 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.
…e a variável onde armazenaremos o código de segmento que será informado pelo usuário na linha de comando. Destacaremos essa variável num item de grupo específico, com o prefixo WA-:
01 WA-ARGUMENTOS. 03 WA-CD-SEGMENTO PIC X(003) VALUE SPACES.
Em seguida precisamos criar a tabela interna que associará o código ao nome de cada segmento. Será nessa tabela que buscaremos o nome que aparecerá no cabeçalho do relatório. Sua estrutura será assim:
O item de grupo SEGMENTO ocorre três vezes e é formado pelos itens elementares CD-SEGMENTO e NM-SEGMENTO. Usaremos um alfanumérico com tamanho 3 para CD-SEGMENTO e um alfanumérico com tamanho 10 para NM-SEGMENTO. Nossa tabela interna ficará assim:
01 WV-TABELA-SEGMENTOS.
03 FILLER PIC X(013) VALUE
"INDINDUSTRIA".
03 FILLER PIC X(013) VALUE
"SRVSERVICO".
03 FILLER PIC X(013) VALUE
"VARVAREJO".
01 FILLER REDEFINES WV-TABELA-SEGMENTOS.
03 WV-SEGMENTO OCCURS 3 INDEXED BY WV-IX.
05 WV-CD-SEGMENTO PIC X(003).
05 WV-NM-SEGMENTO PIC X(010).
As quatro primeiras linhas do relatório seguem o layout padrão que usamos em exemplos anteriores; mudam apenas o nome do relatório (CRL0501) e seu título na linha 3:
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 "FOLHA PAGTO". 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(030) VALUE "CRL0501". 03 FILLER PIC X(036) VALUE "CLIENTES POR SEGMENTO". 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 "-".
Mas esse cabeçalho precisa de uma linha a mais para mostrar o nome do segmento cujo código foi informado pelo usuário. O “CAB4” terá apenas um label (“SEGMENTO: “) e uma variável para onde moveremos esse nome:
01 WR-CAB4. 03 FILLER PIC X(010) VALUE "SEGMENTO: ". 03 WR-CAB-NM-SEGMENTO PIC X(010) VALUE SPACES.
Em seguida vem o cabeçalho com os títulos das colunas, a linha de detalhe e o rodapé onde mostraremos a quantidade de clientes listados:
01 WR-CAB5. 03 FILLER PIC X(009) VALUE "CODIGO". 03 FILLER PIC X(007) VALUE "CLIENTE". 01 WR-DET1. 03 WR-DET-CD-CLIENTE PIC X(006) VALUE SPACES. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-NM-CLIENTE PIC X(045) VALUE SPACES. 01 WR-ROD1. 03 FILLER PIC X(019) VALUE "CLIENTES LISTADOS: ". 03 WR-ROD-CT-CLIENTES PIC ZZZ.ZZ9 VALUE ZEROS.
Na apresentação do diagrama estruturado dissemos que o parágrafo 2-PROCESSA será executado até que não existam mais arquivos ou arquivo de entrada OU que o código de segmento do registro lido seja diferente do código informado pelo usuário. Para implementar isso, incluiremos uma condição na cláusula UNTIL do PERFORM que chama esse parágrafo:
*================================================================*
PROCEDURE DIVISION.
*----------------------------------------------------------------*
0-PRINCIPAL.
PERFORM 1-INICIA
PERFORM 2-PROCESSA
UNTIL WT-ST-CRA0201 NOT = "00"
OR CRA0201-CD-SEGMENTO NOT = WA-CD-SEGMENTO
PERFORM 3-TERMINA
STOP RUN.
No parágrafo 1-INICIA colocaremos o comando que recebe o argumento informado pelo usuário na linha de comando:
*----------------------------------------------------------------*
* OBTEM ARGUMENTO DA LINHA DE COMANDO. ABRE OS ARQUIVOS E POSI-
* CIONA O ARQUIVO DE ENTRADA NO PRIMEIRO REGISTRO CUJO CODIGO DE
* SEGMENTO SEJA IGUAL AO ARGUMENTO INFORMADO. EFETUA A PRIMEIRA
* LEITURA DO ARQUIVO DE ENTRADA.
*----------------------------------------------------------------*
1-INICIA.
ACCEPT WA-CD-SEGMENTO FROM COMMAND-LINE
O comando ACCEPT … FROM COMMAND-LINE traz para o programa qualquer argumento que tenha sido fornecido pelo usuário na linha de comando que chamou o programa. Por exemplo, supondo que o programa esteja sendo executado no Linux e o usuário tenha digitado…
~/bin/CRP0501 SRV
…o comando ACCEPT recuperará o argumento “SRV” e o guardará na variável indicada.
Antes de ler o primeiro registro do cadastro de clientes, usaremos o comando START para posicionar o ponteiro desse arquivo no próximo registro cujo código seja maior ou igual ao código informado pelo usuário:
OPEN INPUT CRA0201 OPEN OUTPUT CRL0501 MOVE WA-CD-SEGMENTO TO CRA0201-CD-SEGMENTO START CRA0201 KEY NOT LESS THAN CRA0201-CHAVE-3 READ CRA0201 NEXT.
Agora vamos voltar um pouco ao conceito do comando START só para entender exatamente o que pode acontecer no momento desse primeiro READ. Suponha que o cadastro de clientes tenha o seguinte conteúdo e que o usuário tenha informado o código “SRV” na linha de comando:
Depois de executar o comando START o programa procura na área de índices o primeiro registro que tenha “SRV” como valor da chave. O próximo READ, portanto, descobriria que esse registro está na posição 7 e recuperaria os dados que estivessem nessa posição.
Agora imagine que o usuário tenha preenchido o argumento com “TTT”. Nesse caso, o comando START posicionaria o ponteiro no próximo registro cujo campo CD-SEGMENTO seja maior ou igual a “TTT”:
No nosso exemplo, não existe nenhum registro associado ao valor “TTT”. O primeiro segmento maior ou igual a “TTT” é o “VAR”, que aponta para o registro 1, e o próximo comando READ vai recuperar os campos desse registro. O parágrafo 2-PROCESSA deve ser executado até o fim do arquivo OU que código de segmento seja diferente do segmento informado. Como a primeira leitura já trouxe um registro com código diferente, nada será impresso no relatório.
Vejamos outra possibilidade: o usuário informa “ZZZ” como código de segmento:
Não existe registro com segmento maior ou igual a “ZZZ” e o ponteiro do arquivo será posicionado depois do último registro. O próximo comando READ vai retornar file status igual a “10”. Mais uma vez, o parágrafo 2-PROCESSA não será executado pois a primeira leitura já provocou uma situação semelhante a fim de arquivo.
Esse é o comportamento que se espera do programa. Esses exemplos procuraram mostrar que o START com a condição NOT LESS THAN (maior ou igual) é aplicável à maioria das situações em que precisamos ler um subconjunto de registros de um arquivo de entrada, sem nos preocuparmos com tratamentos de INVALID KEYs ou IFs adicionais.
O parágrafo 2-PROCESSA precisa apenas verificar se o limite de linhas de página foi atingido, imprimir a linha detalhe e ler o próximo registro do arquivo de entrada. Observe que é aqui que incrementaremos o contador de clientes listados:
*----------------------------------------------------------------*
* IMPRIME TODOS OS CLIENTES SELECIONADOS
*----------------------------------------------------------------*
2-PROCESSA.
IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA
PERFORM X1-IMPRIME-CABECALHO
END-IF
MOVE CRA0201-CD-CLIENTE TO WR-DET-CD-CLIENTE
MOVE CRA0201-NM-CLIENTE TO WR-DET-NM-CLIENTE
WRITE CRL0501-REGISTRO FROM WR-DET1
ADD 1 TO WT-CT-LINHAS
ADD 1 TO WT-CT-CLIENTES
READ CRA0201 NEXT.
E como o relatório tem um rodapé que só vai aparecer no final, podemos codificar sua impressão no próprio parágrafo 3-TERMINA:
*----------------------------------------------------------------* * IMPRIME RODAPE E FECHA ARQUIVOS *----------------------------------------------------------------* 3-TERMINA. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF MOVE WT-CT-CLIENTES TO WR-ROD-CT-CLIENTES WRITE CRL0501-REGISTRO FROM WR-SEP1 WRITE CRL0501-REGISTRO FROM WR-ROD1 WRITE CRL0501-REGISTRO FROM WR-SEP1 CLOSE CRA0201 CRL0501.
O último parágrafo do programa é aquele que imprime as linhas de cabeçalho. A diferença em relação a exemplos que vimos anteriormente é que, aqui, teremos que procurar o nome do segmento na tabela interna. Faremos isso usando um dos formatos do comando SEARCH:
*----------------------------------------------------------------* * 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 SEARCH WV-SEGMENTOS VARYING WV-IX WHEN WV-CD-SEGMENTO(WV-IX) = WA-CD-SEGMENTO MOVE WV-NM-SEGMENTO(WV-IX) TO WR-CAB-NM-SEGMENTO END-SEARCH WRITE CRL0501-REGISTRO FROM WR-CAB1 AFTER PAGE WRITE CRL0501-REGISTRO FROM WR-CAB2 WRITE CRL0501-REGISTRO FROM WR-CAB3 WRITE CRL0501-REGISTRO FROM WR-SEP1 WRITE CRL0501-REGISTRO FROM WR-CAB4 WRITE CRL0501-REGISTRO FROM WR-SEP1 WRITE CRL0501-REGISTRO FROM WR-CAB5 WRITE CRL0501-REGISTRO FROM WR-SEP1 MOVE 8 TO WT-CT-LINHAS.
O comando SEARCH … VARYING é aplicado sobre o item de grupo WV-SEGMENTOS, que é quem foi declarado com a cláusula OCCURS. Ele é formado por dois itens elementares (WV-CD-SEGMENTO e WV-NM-SEGMENTO). Quando o SEARCH encontra um código de segmento igual ao código do argumento, o próprio comando move o nome correspondente para o cabeçalho.
Testando esse programa para o segmento “VAR” geramos o seguinte relatório:
EMPRESA XYZ DATA: 21/01/17 FOLHA PAGTO HORA: 09:03:55 CRL0501 CLIENTES POR SEGMENTO PAGINA: 1 -------------------------------------------------------------------------------- SEGMENTO: VAREJO -------------------------------------------------------------------------------- CODIGO CLIENTE -------------------------------------------------------------------------------- 000515 FARID ELETROMOVEIS 000901 SUPERMERCADO ABC 000988 PAPELARIA OURO PRETO 001101 SUPERMERCADO ITAMARKET 002005 CASA COLORIDA MOVEIS 002201 TEREPOINT PECAS E AUTOPECAS -------------------------------------------------------------------------------- CLIENTES LISTADOS: 6 -------------------------------------------------------------------------------- Observe agora o que seria gerado se informássemos “TTT” ou “ZZZ” na linha de comando: EMPRESA XYZ DATA: 24/01/17 FOLHA PAGTO HORA: 07:29:34 CRL0501 CLIENTES POR SEGMENTO PAGINA: 1 -------------------------------------------------------------------------------- SEGMENTO: -------------------------------------------------------------------------------- CODIGO CLIENTE -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- CLIENTES LISTADOS: 0 --------------------------------------------------------------------------------
Procure acompanhar o programa, comando a comando, para verificar exatamente em que momento e em que condições cada uma das linhas acima foi impressa. O programa completo segue abaixo:
*================================================================* IDENTIFICATION DIVISION. *----------------------------------------------------------------* PROGRAM-ID. CRP0501. AUTHOR. PAULO ANDRE DIAS. DATE-WRITTEN. 16/01/2017. REMARKS. *----------------------------------------------------------------* * SISTEMA: CR - CONTAS A RECEBER * JOB: 05 - RELATORIOS * PROGRAMA: 01 - RELACAO DE CLIENTES POR SEGMENTO * * OBJETIVO: LISTAR CODIGO E NOME DE TODOS OS CLIENTES CUJO * SEGMENTO TENHA SIDO INFORMADO VIA ARGUMENTO * * VERSOES: DATA DESCRICAO * ------ --------------------------------------- * XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *----------------------------------------------------------------* *================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * SALARIOS COPY CRS0201. * RELACAO DE CLIENTES POR SEGMENTO SELECT CRL0501 ASSIGN TO "$DAT/CRL0501.TXT" ORGANIZATION IS LINE SEQUENTIAL. *================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * SALARIOS COPY CRF0201. * RELACAO DE CLIENTES POR SEGMENTO FD CRL0501. 01 CRL0501-REGISTRO PIC X(080). *================================================================* WORKING-STORAGE SECTION. *----------------------------------------------------------------* 01 WC-CONSTANTES. 03 WC-LINHAS-POR-PAGINA PIC 9(002) VALUE 60. 01 WT-FILE-STATUS. 03 WT-ST-CRA0201 PIC X(002) VALUE SPACES. 01 WT-CONTADORES. 03 WT-CT-PAGINA PIC 9(003) VALUE ZEROS. 03 WT-CT-LINHAS PIC 9(002) VALUE 99. 03 WT-CT-CLIENTES PIC 9(006) VALUE ZEROS. 01 WT-AUXILIARES. 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 WA-ARGUMENTOS. 03 WA-CD-SEGMENTO PIC X(003) VALUE SPACES. 01 WV-TABELA-SEGMENTOS. 03 FILLER PIC X(013) VALUE "INDINDUSTRIA". 03 FILLER PIC X(013) VALUE "SRVSERVICO". 03 FILLER PIC X(013) VALUE "VARVAREJO". 01 FILLER REDEFINES WV-TABELA-SEGMENTOS. 03 WV-SEGMENTOS OCCURS 3 INDEXED BY WV-IX. 05 WV-CD-SEGMENTO PIC X(003). 05 WV-NM-SEGMENTO PIC X(010). 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 "FOLHA PAGTO". 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(030) VALUE "CRL0501". 03 FILLER PIC X(036) VALUE "CLIENTES POR SEGMENTO". 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(010) VALUE "SEGMENTO: ". 03 WR-CAB-NM-SEGMENTO PIC X(010) VALUE SPACES. 01 WR-CAB5. 03 FILLER PIC X(009) VALUE "CODIGO". 03 FILLER PIC X(007) VALUE "CLIENTE". 01 WR-DET1. 03 WR-DET-CD-CLIENTE PIC X(006) VALUE SPACES. 03 FILLER PIC X(003) VALUE SPACES. 03 WR-DET-NM-CLIENTE PIC X(045) VALUE SPACES. 01 WR-ROD1. 03 FILLER PIC X(019) VALUE "CLIENTES LISTADOS: ". 03 WR-ROD-CT-CLIENTES PIC ZZZ.ZZ9 VALUE ZEROS. *================================================================* PROCEDURE DIVISION. *----------------------------------------------------------------* 0-PRINCIPAL. PERFORM 1-INICIA PERFORM 2-PROCESSA UNTIL WT-ST-CRA0201 NOT = "00" OR CRA0201-CD-SEGMENTO NOT = WA-CD-SEGMENTO PERFORM 3-TERMINA STOP RUN. *----------------------------------------------------------------* * OBTEM ARGUMENTO DA LINHA DE COMANDO. ABRE OS ARQUIVOS E POSI- * CIONA O ARQUIVO DE ENTRADA NO PRIMEIRO REGISTRO CUJO CODIGO DE * SEGMENTO SEJA IGUAL AO ARGUMENTO INFORMADO. EFETUA A PRIMEIRA * LEITURA DO ARQUIVO DE ENTRADA. *----------------------------------------------------------------* 1-INICIA. ACCEPT WA-CD-SEGMENTO FROM COMMAND-LINE OPEN INPUT CRA0201 OPEN OUTPUT CRL0501 MOVE WA-CD-SEGMENTO TO CRA0201-CD-SEGMENTO START CRA0201 KEY NOT LESS THAN CRA0201-CHAVE-3 READ CRA0201 NEXT. *----------------------------------------------------------------* * IMPRIME TODOS OS CLIENTES SELECIONADOS *----------------------------------------------------------------* 2-PROCESSA. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF MOVE CRA0201-CD-CLIENTE TO WR-DET-CD-CLIENTE MOVE CRA0201-NM-CLIENTE TO WR-DET-NM-CLIENTE WRITE CRL0501-REGISTRO FROM WR-DET1 ADD 1 TO WT-CT-LINHAS ADD 1 TO WT-CT-CLIENTES READ CRA0201 NEXT. *----------------------------------------------------------------* * IMPRIME RODAPE E FECHA ARQUIVOS *----------------------------------------------------------------* 3-TERMINA. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF MOVE WT-CT-CLIENTES TO WR-ROD-CT-CLIENTES WRITE CRL0501-REGISTRO FROM WR-SEP1 WRITE CRL0501-REGISTRO FROM WR-ROD1 WRITE CRL0501-REGISTRO FROM WR-SEP1 CLOSE CRA0201 CRL0501. *----------------------------------------------------------------* * 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 SEARCH WV-SEGMENTOS VARYING WV-IX WHEN WV-CD-SEGMENTO(WV-IX) = WA-CD-SEGMENTO MOVE WV-NM-SEGMENTO(WV-IX) TO WR-CAB-NM-SEGMENTO END-SEARCH WRITE CRL0501-REGISTRO FROM WR-CAB1 AFTER PAGE WRITE CRL0501-REGISTRO FROM WR-CAB2 WRITE CRL0501-REGISTRO FROM WR-CAB3 WRITE CRL0501-REGISTRO FROM WR-SEP1 WRITE CRL0501-REGISTRO FROM WR-CAB4 WRITE CRL0501-REGISTRO FROM WR-SEP1 WRITE CRL0501-REGISTRO FROM WR-CAB5 WRITE CRL0501-REGISTRO FROM WR-SEP1 MOVE 8 TO WT-CT-LINHAS.
Anterior | Conteúdo | Próxima |