6. Procedure Division

6. Procedure Division

Até aqui vimos três das quatro divisões que compõem um programa COBOL, na ordem em que elas devem ser codificadas. A última divisão, que veremos neste capítulo, é onde ficam as instruções que serão executadas pelo programa. Toda a lógica programação estará nessas instruções.

Recapitulando, a IDENTIFICATION DIVISION contém informações documentacionais sobre o programa. A ENVIRONMENT descreve o ambiente em que o programa será executado, indicando também os arquivos que serão acessados. A DATA DIVISION detalha esses arquivos e declara variáveis de trabalho. É na PROCEDURE DIVISION, finalmente, que os arquivos são lidos e processados para produzir alguma informação de saída.

Neste capítulo veremos como abrir arquivos de entrada e saída, como ler e escrever informações nesses arquivos, como atribuir valores aos campos dos arquivos e às variáveis de trabalho, como realizar operações aritméticas, como controlar o fluxo de execução do programa e encerrar sua execução. O conhecimento dessas instruções é suficiente para construção de programas completos, ainda que elementares.

Parágrafos e sentenças

Como em todas as outras divisões, a PROCEDURE é dividida em parágrafos. Cada parágrafo deve ter um nome atribuído pelo programador, e esse nome deve ser único dentro do programa.

Os nomes dos parágrafos podem ser formados por letras, números e hífens. E, naturalmente, cada empresa estabelece seu próprio padrão de nomenclatura.

Dentro de cada parágrafo estarão as sentenças (ou instruções) do programa. Toda sentença começa com um verbo em inglês: READ, MOVE, WRITE, ADD… A única exceção para essa regra é a instrução que usamos para testar condições (IF).

Os nomes dos parágrafos devem ser codificados a partir da coluna 8 (área A) e terminar obrigatoriamente com um ponto. As sentenças devem ser codificadas a partir da coluna 12 (área B). Apenas a última sentença de um parágrafo precisa de um ponto final. O COBOL permite que se use várias sentenças numa única linha de programa, mas essa prática não é recomendada pois dificulta a leitura do programa.

Parágrafos assumem papeis diferentes, dependendo do paradigma de codificação que será usado pelo programador. Na programação linear (em desuso), parágrafos funcionam como labels ou tags que marcam um ponto para onde o fluxo de execução do programa pode ser desviado. Na programação estruturada (praticamente obrigatória em todas as instalações) parágrafos atuam como rotinas que têm uma finalidade específica, como se fossem métodos ou funções (ainda que esses dois conceitos não se apliquem diretamente ao COBOL).

Falaremos sobre programação linear e programação estruturada mais adiante. Por ora, iremos apenas ilustrar esses modelos com dois exemplos simples.

PARAGRAFO-1.

    SENTENÇA-1
    SENTENÇA-2
    SENTENÇA-3.

PARAGRAFO-2.

    SE CONDIÇAO-1
       GO TO PARAGRAFO-3
    FIM-SE

    SENTENÇA-4
    SENTENÇA-5
    SENTENÇA-6

    GO TO PARAGRAFO-2.

PARAGRAFO-3.

    SENTENÇA-7
    SENTENÇA-8
    SENTENÇA-9

    FIM DO PROGRAMA.

O pseudocódigo acima mostra como os parágrafos são usados na programação linear. Perceba que PARAGRAFO-2 e PARAGRAFO-3 são apenas pontos de referência para onde o fluxo de execução é desviado com um comando GO TO. PARAGRAFO-2 claramente é um loop, que termina quando a CONDIÇAO-1 é satisfeita.

Na programação estruturada, por outro lado, parágrafos são usados para organizar o programa em blocos que possuem uma finalidade única, (preferencialmente), como veremos no trecho a seguir:

INICIO.

    EXECUTE PARAGRAFO-1
    EXECUTE PARAGRAFO-2 ATE’ CONDIÇAO-1
    EXECUTE PARAGRAFO-3

    FIM DO PROGRAMA.

PARAGRAFO-1.

    SENTENÇA-1
    SENTENÇA-2
    SENTENÇA-3.

PARAGRAFO-2.

    SENTENÇA-4
    SENTENÇA-5
    SENTENÇA-6.

PARAGRAFO-3.

    SENTENÇA-7
    SENTENÇA-8
    SENTENÇA-9.

Repare que estamos executando as mesmas instruções que vimos no modelo linear (SENTENÇA-1, SENTENÇA-2…). Mas agora essas sentenças estão organizadas em blocos que são executados no início do programa. PARAGRAFO-2 é executado até que a CONDIÇÃO-1 seja satisfeita: é o mesmo loop que vimos anteriormente mas, nesse caso, não precisamos usar o comando GO TO.

É possível perceber que um grande programa linear, com diversos GO TO’s para todo lado, pode ser muito fácil de codificar, mas muito mais difícil de entender no futuro. A programação estruturada, porém, exige que o programador “pense” um pouco mais antes de começar a codificar, mas o entendimento do programa fica muito mais fácil no futuro.

Nas próximas seções, apenas para efeito didático, codificaremos nosso primeiro programa seguindo uma lógica linear. No capítulo seguinte falaremos mais sobre programação estruturada e reconstruiremos nosso programa usando esse modelo.

Construindo o primeiro programa

Vamos imaginar um programa que tenha por objetivo ler um arquivo sequencial, selecionar alguns registros e gravar os registros selecionados num arquivo sequencial de saída. Para isso aproveitaremos tudo o que fizemos nos capítulos anteriores.

Juntando a IDENTIFICATION, a ENVIRONMENT e a DATA DIVISION nosso programa, neste momento, estaria assim:

      *================================================================*
       IDENTIFICATION DIVISION.
      *----------------------------------------------------------------*
       PROGRAM-ID.    CRP0206.
       AUTHOR.        PAULO ANDRE DIAS.
       DATE-WRITTEN.  23/12/2016.
       REMARKS.
      *----------------------------------------------------------------*
      * SISTEMA:      CR - CONTAS A RECEBER
      * JOB:          02 - GERACAO DE FLUXO DE CAIXA
      * PROGRAMA:     06 - SELECIONA DUPLICATAS ATIVAS
      *
      * OBJETIVO:     GERAR ARQUIVO DE SAIDA QUE CONTEM APENAS AS DU-
      *               PLICATAS QUE NAO FORAM CANCELADAS (SITUACAO DA
      *               DUPLICATA DEVE SER DIFERENTE DE "CNC")
      *
      * VERSOES:      DATA    DESCRICAO
      *               ------  ---------------------------------------
      *               XXXXXX  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      *
      *----------------------------------------------------------------*

      *================================================================*
       ENVIRONMENT DIVISION.
      *----------------------------------------------------------------*
       CONFIGURATION SECTION.
       SPECIAL-NAMES.
           DECIMAL-POINT IS COMMA.

       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
      *----------------------------------------------------------------*
      * UNLOAD DA TABELA DE DUPLICATAS
      *----------------------------------------------------------------*
           SELECT CRA0205 ASSIGN TO "../dat/cra0205.dat"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WT-ST-CRA0205.

      *----------------------------------------------------------------*
      * DUPLICATAS ATIVAS EMITIDAS NO PERIODO
      *----------------------------------------------------------------*
           SELECT CRA0206 ASSIGN TO "../dat/cra0206.dat"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WT-ST-CRA0206.

      *================================================================*
       DATA DIVISION.
      *----------------------------------------------------------------*
       FILE SECTION.
      *----------------------------------------------------------------*
      * UNLOAD DA TABELA DE DUPLICATAS
      *----------------------------------------------------------------*
       FD CRA0205.
       01 CRA0205-REGISTRO.
           03 CRA0205-NR-FATURA        PIC  9(006).
           03 CRA0205-NR-DUPLICATA     PIC  9(002).
           03 CRA0205-CD-CLIENTE       PIC  X(006).
           03 CRA0205-DT-EMISSAO       PIC  9(008).
           03 CRA0205-DT-VENCIMENTO    PIC  9(008).
           03 CRA0205-VL-FATURA        PIC S9(013)V9(002).
           03 CRA0205-CD-CATEGORIA     PIC  X(003).
           03 CRA0205-ST-DUPLICATA     PIC  X(003).

      *----------------------------------------------------------------*
      * DUPLICATAS ATIVAS EMITIDAS NO PERIODO
      *----------------------------------------------------------------*
       FD CRA0206.
       01 CRA0206-REGISTRO.
           03 CRA0206-NR-FATURA        PIC  9(006).
           03 CRA0206-NR-DUPLICATA     PIC  9(002).
           03 CRA0206-CD-CLIENTE       PIC  X(006).
           03 CRA0206-DT-EMISSAO       PIC  9(008).
           03 CRA0206-DT-VENCIMENTO    PIC  9(008).
           03 CRA0206-VL-FATURA        PIC S9(013)V9(002).
           03 CRA0206-CD-CATEGORIA     PIC  X(003).
           03 CRA0206-ST-DUPLICATA     PIC  X(003).

      *================================================================*
       WORKING-STORAGE SECTION.
      *----------------------------------------------------------------*
       01 WT-CONTADORES.
           03 WT-CT-LIDOS              PIC  9(006) VALUE ZEROS.
           03 WT-CT-GRAVADOS           PIC  9(006) VALUE ZEROS.

       01 WT-FILE-STATUS.
           03 WT-ST-CRA0205  PIC X(002) VALUE SPACES.
           03 WT-ST-CRA0206  PIC X(002) VALUE SPACES.

A PROCEDURE DIVISION começa imediatamente depois da WORKING-STORAGE SECTION, e como todas as divisões ela deve ser declarada a partir da coluna 8. Logo em seguida, criaremos o primeiro parágrafo do programa, que chamaremos de INICIO-DO-PROGRAMA, também na coluna 8:

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       INICIO-DO-PROGRAMA.

Nosso programa vai ler todos os registros do arquivo de entrada (CRA0205), selecionar apenas os registros cujo campo ST-DUPLICATA seja diferente de “CNC” (cancelado) e gravar os registros selecionados no arquivo de saída (CRA0206). Antes de terminar, o programa deverá mostrar a quantidade de registros lidos e a quantidade de registros gravados.

O fluxograma abaixo detalha um pouco mais essa lógica:

Cobol: Fluxograma
Figura 10. Fluxo do programa CRP0206

Abertura de arquivos

Todo arquivo precisa ser “aberto” pelo programa para que possamos ler ou gravar registros nele. Qualquer operação de leitura ou escrita de registros num arquivo que não foi previamente aberto provocará um erro de execução que será sinalizado no file status correspondente.

A abertura de arquivos é comandada pela instrução OPEN, que tem o seguinte formato:

OPEN modo nome-do-arquivo

O operador “modo” informa se vamos acessar o arquivo para leitura (INPUT) ou gravação (OUTPUT). O “nome do arquivo” que aparece no comando OPEN deve ser igual ao nome informado na cláusula FD da FILE SECTION.

Existem outros modos possíveis nesse comando. OPEN I-O abre um arquivo para leitura e gravação; esse é o modo que nos permite, por exemplo, atualizar (REWRITE) os registros de um arquivo. OPEN EXTEND nos permite inserir registros no final de um arquivo já existente. Falaremos mais sobre esses dois modos mais adiante neste livro. Por enquanto é importante notar que OPEN I-O e OPEN EXTEND preservam o conteúdo existente no arquivo. Já OPEN OUTPUT apaga todo o conteúdo existente e prepara o arquivo para a gravação de novos registros.

O COBOL permite que um único comando OPEN seja usado para abrir vários arquivos de leitura e gravação ao mesmo tempo:

OPEN INPUT  arquivo1 arquivo2 arquivo3
     OUTPUT arquivo4 arquivo5 arquivo6
     I-O    arquivo7 arquivo8 arquivo9

No exemplo acima, arquivo1, arquivo2 e arquivo3 estão sendo abertos para leitura; arquivo4, arquivo5 e arquivo6 estão sendo abertos para gravação; arquivo7, arquivo8 e arquivo9 estão sendo abertos tanto para operações de leitura quanto de gravação.

Sabemos que nosso programa precisa ler todos os registros do arquivo CRA0205 e gravar os registros selecionados no arquivo CRA0206. Portanto, nossas primeiras instruções ficariam assim:

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       INICIO-DO-PROGRAMA.

           OPEN INPUT CRA0205
           OPEN OUTPUT CRA0206

Que podem ser unidos num só comando...

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       INICIO-DO-PROGRAMA.

           OPEN INPUT CRA0205 OUTPUT CRA0206

Leitura de arquivos

READ é o comando COBOL responsável por ler o conteúdo de arquivos. Seu formato mais simples é:

READ nome-do-arquivo

O nome do arquivo fornecido ao comando deve ser igual ao nome que usamos no comando OPEN.

Quando usado com arquivos sequenciais, a leitura acontece registro a registro, isto é, cada vez que o comando READ é executado, o programa busca o próximo registro disponível no arquivo e copia seu conteúdo para o item de grupo (nível 01) que definimos na FILE SECTION.

Usando nosso programa como exemplo, o arquivo CRA0205 foi declarado na ENVIRONMENT DIVISION e apontado para um arquivo sequencial cujo nome real é “../dat/cra0205.dat”:

           SELECT CRA0205 ASSIGN TO "../dat/cra0205.dat"
               ORGANIZATION IS LINE SEQUENTIAL.

Em seguida, detalhamos esse arquivo na FILE SECTION da DATA DIVISION:

       FD CRA0205.
       01 CRA0205-REGISTRO.
           03 CRA0205-NR-FATURA        PIC  9(006).
           03 CRA0205-NR-DUPLICATA     PIC  9(002).
           03 CRA0205-CD-CLIENTE       PIC  X(006).
           03 CRA0205-DT-EMISSAO       PIC  9(008).
           03 CRA0205-DT-VENCIMENTO    PIC  9(008).
           03 CRA0205-VL-FATURA        PIC S9(013)V9(002).
           03 CRA0205-CD-CATEGORIA     PIC  X(003).
           03 CRA0205-ST-DUPLICATA     PIC  X(003).

Agora, na PROCEDURE DIVISION, codificaremos o comando READ da seguinte maneira:

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       INICIO-DO-PROGRAMA.

           OPEN INPUT CRA0205 OUTPUT CRA0206

           READ CRA0205

Quando este comando for executado pela primeira vez, o conteúdo do primeiro registro do arquivo “../dat/cra0205.dat” será copiado para o item de grupo CRA0205-REGISTRO.

Já vimos que quando um item de grupo é preenchido, seus itens elementares assumem os valores que estão nas posições correspondentes. Se o primeiro registro do arquivo for…

11111122ABCDEF2015102520151125000000000012300QRTATV

O registro inteiro será copiado para CRA0205-REGISTRO e, consequentemente, seus itens elementares assumirão os seguintes valores:

CRA0205-NR-FATURA = 111111
CRA0205-NR-DUPLICATA = 22
CRA0205-CD-CLIENTE = ABCDEF
CRA0205-DT-EMISSAO = 20151025
CRA0205-DT-VENCIMENTO = 20151125
CRA0205-VL-FATURA = 123,00
CRA0205-CD-CATEGORIA = QRT
CRA0205-ST-DUPLICATA = ATV

Cada vez que o comando READ CRA0205 for executado, o programa buscará o próximo registro do arquivo e copiará o conteúdo desse registro para CRA0205-REGISTRO, substituindo os valores anteriores.

Testando condições

O IF é o comando que utilizamos quando queremos testar uma condição em COBOL. Seu formato mais simples é:

IF condição
   Comando(s)
ELSE
   Comando(s)
END-IF

A cláusula END-IF pode ser substituída por um ponto e talvez você ainda encontre programas assim, principalmente se foram escritos para compiladores anteriores ao COBOL/85. No entanto, o uso de END-IF é preferível pois aumenta a flexibilidade para a construção de IFs aninhados:

IF condição-1
   Comando(s)
   IF condiçãoo-2
      Comando(s)
   ELSE
      Comando(s)
   END-IF
ELSE
   Comando(s)
   IF Condição-3
      Comando(s)
   ELSE
      Comando(s)
   END-IF
END-IF

As condições testadas pelo IF normalmente envolvem dois operandos (variáveis, literais ou fórmulas) e um conector de relacionamento: GREATER THAN (>), LESS THAN (<) ou EQUAL TO (=).

Exemplos:

IF CRA0205-VL-FATURA > ZEROS

IF CRA0205-VL-FATURA IS GREATER THAN ZEROS

Essas duas linhas estão fazendo exatamente o mesmo tipo de teste; a única diferença é que na linha de baixo se escreve mais… resquícios da época em que o COBOL queria se parecer com um texto escrito em inglês.

É possível também “negar” esses conectores colocando a palavra NOT na frente deles. Por exemplo:

IF CRA0205-VL-FATURA NOT > 1000

IF CRA0205-DT-EMISSAO NOT = ZEROS

IF CRA0205-CD-CATEGORIA NOT = SPACES

Os operadores lógicos AND e OR podem ser usados para criação de condições compostas:

IF CRA0205-DT-EMISSAO NOT = ZEROS AND
   CRA0205-VL-FATURA > 1000
   DISPLAY “CONDICAO VERDADEIRA”
ELSE
   DISPLAY “CONDICAO FALSA”
END-IF

No exemplo acima, o texto “CONDICAO VERDADEIRA” será exibido na tela se, e somente se, CRA0205-DT-EMISSAO for diferente de zeros e CRA0205-VL-FATURA for maior que 1000. Se o operador lógico fosse OR…

IF CRA0205-DT-EMISSAO NOT = ZEROS OR
   CRA0205-VL-FATURA > 1000
   DISPLAY “CONDICAO VERDADEIRA”
ELSE
   DISPLAY “CONDICAO FALSA”
END-IF

O texto “CONDICAO VERDADEIRA” seria exibido tanto se CRA0205-EMISSAO fosse diferente de zeros quanto se CRA0205-VL-FATURA fosse maior que zeros.

Como em muitas outras linguagens, AND tem precedência sobre OR. Para ter mais controles sobre a precedência das validações usamos parênteses para separar as condições

IF (WS-NR-OPCAO = 1 OR WS-NR-OPCAO = 2) AND WS-TX-RSP = “S”
   faz alguma coisa
END-IF

Nesse exemplo, se a variável WS-NR-OPCAO for igual a 1 ou 2 a condição representada entre parênteses será verdadeira. Só depois dessa avaliação é que o programa verificará se WS-TX-RSP = “S”.

Voltando a nosso programa exemplo, sabemos que só serão gravados no arquivo de saída os registros do arquivo CRA0205 que estiverem com o campo ST-DUPLICATA diferente de “CNC”. Logo, depois da leitura do arquivo precisamos inserir um IF para testar esse campo:

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       INICIO-DO-PROGRAMA.

           OPEN INPUT CRA0205 OUTPUT CRA0206

           READ CRA0205

           IF CRA0205-ST-DUPLICATA NOT = “CNC”
              {Mover campos para registro de saída}
              {Gravar registro no arquivo de saída}
           END-IF

Nas próximas seções veremos como preencher o registro do arquivo de saída e gravar esse registro no arquivo CRA0206.

Atribuindo valores a variáveis

O comando MOVE é o responsável por atribuir valores a variáveis. O formato abaixo copia o conteúdo da variável1 para a variável2:

MOVE variável1 TO variável2

O verbo “MOVE” pode ser enganoso num primeiro momento pois pode dar a impressão de que o conteúdo está sendo “movido”, ou transferido, de uma variável para outra. Na verdade o conteúdo está sendo “copiado” de uma variável para a outra.

O MOVE permite também que variáveis sejam preenchidas com literais ou constantes figurativas:

MOVE “Teste” TO WT-NM-FASE
MOVE SPACES TO CRA0205-CD-CLIENTE
MOVE ZEROS TO WT-CT-LIDOS

Você também pode preencher o conteúdo de mais de uma variável com um único comando:

MOVE ZEROS TO WT-CT-LIDOS WT-CT-GRAVADOS
MOVE SPACES TO WT-NM-CLIENTE WT-DE-ENDERECO WT-ST-CLIENTE
MOVE “ABCD” TO WT-TX-1 WT-TX-2 WT-TX-3 WT-TX-4

É possível movimentar valores entre variáveis que tenham tamanhos diferentes e até tipos diferentes. Por default, o conteúdo de variáveis alfanuméricas é alinhado à esquerda, enquanto o conteúdo de variáveis numéricas é alinhado à direita. O valor final na variável de destino, porém, pode variar caso a caso:

Cobol: Resultados da movimentação
Figura 11. Resultados da movimentação dependendo do tipo e do tamanho de cada variável

O primeiro exemplo da figura anterior mostra uma variável de origem (VARIAVEL-A) que é alfanumérica com tamanho 5 e conteúdo “ABCDE”. Ela está sendo movida para uma variável de destino (VARIAVEL-B), também alfanumérica, mas com tamanho maior. O COBOL alinha o conteúdo à esquerda da variável e completa o espaço restante com brancos.

Já na segunda linha temos um exemplo contrário. A variável de origem é maior que a variável de destino. O COBOL alinha o conteúdo à esquerda e trunca os caracteres excedentes.

Esse comportamento é ligeiramente diferente quando as variáveis são numéricas. A terceira linha da figura anterior mostra que a variável de origem é numérica com um tamanho maior que a variável de destino. O COBOL alinha o conteúdo à direita e trunca os caracteres excedentes à esquerda.

Já a quarta linha mostra a situação contrária: a variável numérica de origem tem tamanho menor que a variável de destino. Nesse caso, o COBOL alinha o conteúdo à direita e preenche as posições excedentes à esquerda com zeros.

As duas linhas seguintes mostram o que acontece quando as variáveis são numéricas com casas decimais. A parte inteira é alinhada também pela direita. Caracteres excedentes da variável de origem são truncados à esquerda, caso a parte inteira da variável de destino seja menor. Se a parte inteira da variável de destino for maior, os dígitos à esquerda serão completados com zeros. A parte decimal tem tratamento diferente. Normalmente se a variável de origem tem mais caracteres decimais do que a variável de destino, os dígitos excedentes são truncados à direita. Caso contrário, os dígitos excedentes da parte decimal da variável de destino são completados com zeros.

Os três últimos exemplos mostram o que acontece quando fazemos movimentações entre variáveis que possuem tipos diferentes. A movimentação de uma variável numérica para uma variável alfanumérica é simples. O valor é alinhado à esquerda (porque a variável destino é alfanumérica) e os caracteres excedentes são completados com espaços.

Deve-se evitar fazer movimentações de variáveis alfanuméricas para variáveis numéricas. O MOVE funcionará sem problemas se os tamanhos forem compatíveis e o conteúdo da variável de origem for efetivamente numérico.  Mas se houver caracteres alfabéticos na variável de origem, a execução do programa será cancelada com fim anormal.

Nosso programa exemplo precisa transferir alguns campos do arquivo de entrada CRA0205 para o arquivo de saída CRA0206 para todas as duplicatas que estejam com situação diferente de “CNC”. No tópico anterior nós já colocamos o comando IF que faz esse teste. Agora precisamos movimentar todos os campos do arquivo de entrada que queremos gravar no arquivo de saída.

Nossa PROCEDURE DIVISION ficaria assim:

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       INICIO-DO-PROGRAMA.

           OPEN INPUT CRA0205 OUTPUT CRA0206

           READ CRA0205
           IF CRA0205-ST-DUPLICATA NOT = “CNC”
              MOVE CRA0205-NR-FATURA TO CRA0206-NR-FATURA
              MOVE CRA0205-NR-DUPLICATA TO CRA0206-NR-DUPLICATA
              MOVE CRA0205-CD-CLIENTE TO CRA0206-CD-CLIENTE
              MOVE CRA0205-DT-EMISSAO TO CRA0206-DT-EMISSAO
              MOVE CRA0205-DT-VENCIMENTO TO CRA0206-DT-VENCIMENTO
              MOVE CRA0205-VL-FATURA TO CRA0206-VL-FATURA
              MOVE CRA0205-CD-CATEGORIA TO CRA0206-CD-CATEGORIA
              MOVE CRA0205-ST-DUPLICATA TO CRA0206-ST-DUPLICATA
              {Gravar registro no arquivo de saída}
           END-IF

Gravação de registros em arquivos de saída

Depois de preenchermos um registro é possível gravá-lo num arquivo de saída. Essa gravação só pode acontecer em arquivos que foram abertos em modo de saída (OPEN OUTPUT), entrada e saída (OPEN I-O) ou estendido (OPEN EXTEND).

O comando responsável por gravar registros em arquivos de saída é o WRITE. Seu formato básico é:

WRITE nome-do-registro

Repare que enquanto o comando READ faz referência a um arquivo (READ nome-do-arquivo), o comando WRITE faz referência ao nome do registro, ou seja, ao item de grupo de nível 01 que declaramos logo depois da cláusula FD na FILE SECTION.

O comando WRITE admite alguns outros formatos, como por exemplo:

WRITE nome-do-registro FROM variável

Neste caso, o conteúdo da “variável” será transferido para o registro antes que a gravação aconteça, funcionando exatamente da mesma forma que os comandos abaixo:

MOVE variável TO nome-do-registro
WRITE nome-do-registro

Para gravar o registro no arquivo de saída do nosso exemplo codificaremos o seguinte comando:

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       INICIO-DO-PROGRAMA.

           OPEN INPUT CRA0205 OUTPUT CRA0206

           READ CRA0205
           IF CRA0205-ST-DUPLICATA NOT = “CNC”
              MOVE CRA0205-NR-FATURA TO CRA0206-NR-FATURA
              MOVE CRA0205-NR-DUPLICATA TO CRA0206-NR-DUPLICATA
              MOVE CRA0205-CD-CLIENTE TO CRA0206-CD-CLIENTE
              MOVE CRA0205-DT-EMISSAO TO CRA0206-DT-EMISSAO
              MOVE CRA0205-DT-VENCIMENTO TO CRA0206-DT-VENCIMENTO
              MOVE CRA0205-VL-FATURA TO CRA0206-VL-FATURA
              MOVE CRA0205-CD-CATEGORIA TO CRA0206-CD-CATEGORIA
              MOVE CRA0205-ST-DUPLICATA TO CRA0206-ST-DUPLICATA
              WRITE CRA0206-REGISTRO
           END-IF

Operações aritméticas

As operações aritméticas em COBOL também são realizadas por cinco verbos que descrevem literalmente, em inglês, as operações que pretendemos realizar. Esses verbos são: ADD, SUBTRACT, DIVIDE,  MULTIPLY e COMPUTE.

Para somar um valor fixo a uma variável podemos escrever:

ADD literal TO variável-X

Nesse caso, um valor fixo literal será somado ao valor atual da variável-X. Por exemplo, ADD 1 TO WT-CT-LIDOS, ADD 60 TO WT-CT-LINHAS etc…

O comando ADD nos permite também somar o valor de uma variável a outra:

ADD variável-X TO variável-Y

Também podemos usar vários argumentos no mesmo comando: todos eles serão somados à variável cujo nome aparece depois do TO:

ADD VR-SALARIO VR-BENEFICIO TO WT-VR-RECEITA
ADD WT-VR-DEBITO WT-VR-CREDITO TO WT-VR-SALDO

Um outro formato possível para o comando ADD usa GIVING no lugar de TO:

ADD WT-VR-DEBITO WT-VR-CREDITO GIVING WT-VR-SALDO

Usando GIVING, a soma dos argumentos anteriores substitui o valor existente na variável de destino. No caso acima o programa somará WT-VR-DEBITO e WT-VR-CREDITO e o resultado será copiado para WT-VR-SALDO, cujo valor anterior será perdido.

Para reforçar esses conceitos, vamos ver mais alguns exemplos práticos:

Cobol: Exemplos com o comando ADD
Figura 12. Exemplos com o comando ADD

O comando SUBTRACT faz a operação de subtração. No formato abaixo, um valor literal fixo será subtraído do valor previamente armazenado na variável-X.

SUBTRACT literal FROM variável-X

Também é possível fazer a operação com duas variáveis:

SUBTRACT variável-X FROM variável-Y

E também vários argumentos antes da cláusula FROM:

SUBTRACT variável-X variável-Y FROM variável-Z
SUBTRACT 100 variável-W variável-X variável-Y FROM variável-Z

No exemplo abaixo usamos a cláusula GIVING para que o resultado fique na variável-Z, e não na variável-Y:

SUBTRACT variável-X FROM variável-Y GIVING variável-Z

Vamos ver também alguns exemplos práticos com esse comando:

Cobol: Exemplos com o comando SUBTRACT
Figura 13. Exemplos com o comando SUBTRACT

DIVIDE é o comando usado para operações de divisão, e seus formatos mais simples são:

DIVIDE variável-X BY literal GIVING variável-Y
DIVIDE variável-X BY variável-Y GIVING variável-Z

Esse comando é o único que pode provocar o encerramento do programa com um fim anormal, caso se tente fazer uma divisão por zero. Para impedir que isso aconteça, podemos colocar um IF antes da operação (para verificar se o divisor é zero) ou usar a cláusula ON SIZE ERROR. No exemplo abaixo, se variável-Y estiver zerada a variável-Z receberá o valor 1:

DIVIDE variável-X BY variável-Y GIVING variável-Z
       ON SIZE ERROR MOVE 1 TO variável-Z

Podemos usar o comando DIVIDE para calcular o resto de uma divisão:

DIVIDE variável-X BY variável-Y GIVING variável-Z REMAINDER variável-R

No exemplo acima, o valor da variável-X será dividido pelo valor da variável-Y. O resultado da divisão ficará na variável-Z e o resto da operação, se houver, ficará na variável-R.

Como vimos em capítulos anteriores, a quantidade de casas decimais é determinada pela picture que usamos ao declarar a variável. Essa quantidade de casas decimais será respeitada não só pelo comando DIVIDE mas também por qualquer outro comando aritmético (ADD, SUBTRACT, MULTIPLY ou COMPUTE).

Assim, por exemplo, supondo que uma variável chamada VARIAVEL-X tenha sido definida com 3 casas decimais:

03 VARIAVEL-X PIC 9(006)V9(003) VALUE ZEROS.

Se dividirmos 100 por 7 e colocarmos o resultado na VARIAVEL-X…

DIVIDE 100 BY 7 GIVING VARIAVEL-X

Esta variável ficaria com valor igual a 14,285.

Se a operação de divisão for realizada com variáveis inteiras, o critério padrão do comando DIVIDE é truncar o resultado final. Mas o comando também permite que se defina o critério de arredondamento com o uso da cláusula ROUNDED:

DIVIDE 15 BY 8 GIVING variável-X ROUNDED

No exemplo acima, se variável-X for uma variável inteira (não decimal) ela receberá valor 2 (1,875 arredondado para cima).

O comando DIVIDE possui um segundo formato interessante que usa a cláusula INTO no lugar do BY. Esse formato pode ser útil quando se quer reduzir a quantidade de operandos. No exemplo abaixo, variável-X será dividida por 5 e o resultado será armazenado na própria variável-X.

DIVIDE 5 INTO variável-X

Obviamente também podemos usar uma variável no lugar do numerador. No caso abaixo, a variável-Y será dividida pela variável-X e o resultado ficará em variável-Y:

DIVIDE variável-X INTO variável-Y
Cobol: Exemplos com o comando DIVIDE
Figura 14. Exemplos com o comando DIVIDE

O comando MULTIPLY é usado para calcular o produto de duas quantidades numéricas. Também é possível armazenar o resultado num dos operandos ou numa terceira variável:

MULTIPLY variável-X BY variável-Y

No exemplo acima, variável-Y seria multiplicada pela variável-X e o resultado ficaria na variável-Y. O exemplo abaixo armazena o resultado na variável Z:

MULTIPLY variável-X BY variável-Y GIVING variável-Z

Como nos comandos anteriores, podemos usar literais no lugar de um dos operandos. As duas linhas abaixo têm o mesmo resultado:

MULTIPLY variável-X BY 3 GIVING variável-X
MULTIPLY 3 BY variável-X
Cobol: Exemplos com o comando MULTIPLY
Figura 15. Exemplos com o comando MULTIPLY

Deixamos o comando COMPUTE por último porque provavelmente, agora, você vai considerá-lo mais fácil que os anteriores. Ele oferece um modo mais compacto de especificar operações aritméticas, principalmente quando o programa precisar de fórmulas mais complexas.

Seu formato geral é:

COMPUTE variável-X = fórmula

A fórmula ou expressão aritmética é uma combinação de variáveis, operadores aritméticos e literais. Também podem ser usados parênteses para estabelecer a precedência das operações.

Os operadores aritméticos possíveis são:

  • + para adição
  • – para subtração
  • * para multiplicação
  • / para divisão
  • ** para exponenciação

Se não forem usados parênteses para estabelecer precedências, as operações serão executadas na seguinte ordem:

  1. Exponenciação
  2. Multiplicações e divisões (na ordem em que aparecem na fórmula)
  3. Somas e subtrações (na ordem em que aparecem na fórmula)

É possível usar a exponenciação para cálculos envolvendo raízes. Por exemplo, para calcular a raiz cúbica de A podemos fazer assim:

COMPUTE A = A ** (1 / 3)

Todos os operadores (variáveis ou literais) precisam ser separados por pelo menos um espaço: “A – B” será interpretado como uma expressão aritmética, enquanto “A-B” será interpretado como uma possível variável chamada A-B.

Cobol: Exemplos com o comando COMPUTE
Figura 16. Exemplos com o comando COMPUTE

O COMPUTE normalmente é usado para fórmulas mais complexas, onde sua legibilidade é certamente melhor que do uma grande sequência de comandos aritméticos parciais.

Observe o exemplo abaixo:

COMPUTE A = (B / C) + (D * (E + F)) – G

Usando os comandos individuais ficaria assim:

ADD E F GIVING A
MULTIPLY D BY A
DIVIDE B BY C GIVING X
ADD X TO A
SUBTRACT G FROM A

Repare que neste caso, além de usar cinco comandos ao invés de um, tivemos que criar uma variável intermediária adicional (X).

Houve uma época em que a performance do COMPUTE era pior do que a performance de ADD, SUBTRACT, DIVIDE e MULTIPLY. Hoje, com a evolução de hardware e software, a diferença é imperceptível, se é que existe. Mas você encontrará muitos programas com ADD, SUBTRACT, DIVIDE e MULTIPLY, e muitos programadores que ainda preferem usá-los.

Voltando ao nosso primeiro programa, precisamos contar a quantidade de registros lidos e a quantidade de registros gravados, e exibir essas quantidades no final do programa. Inclusive já criamos duas variáveis numéricas na WORKING-STORAGE que chamamos de WT-CT-LIDOS e WT-CT-GRAVADOS. Agora, precisamos incluir os comandos para fazer essa contagem a cada leitura e a cada gravação:

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       INICIO-DO-PROGRAMA.

           OPEN INPUT CRA0205 OUTPUT CRA0206

           READ CRA0205
           ADD 1 TO WT-CT-LIDOS

           IF CRA0205-ST-DUPLICATA NOT = “CNC”
              MOVE CRA0205-NR-FATURA TO CRA0206-NR-FATURA
              MOVE CRA0205-NR-DUPLICATA TO CRA0206-NR-DUPLICATA
              MOVE CRA0205-CD-CLIENTE TO CRA0206-CD-CLIENTE
              MOVE CRA0205-DT-EMISSAO TO CRA0206-DT-EMISSAO
              MOVE CRA0205-DT-VENCIMENTO TO CRA0206-DT-VENCIMENTO
              MOVE CRA0205-VL-FATURA TO CRA0206-VL-FATURA
              MOVE CRA0205-CD-CATEGORIA TO CRA0206-CD-CATEGORIA
              MOVE CRA0205-ST-DUPLICATA TO CRA0206-ST-DUPLICATA
              WRITE CRA0206-REGISTRO
              ADD 1 TO WT-CT-GRAVADOS
           END-IF

Incluímos os comandos ADD imediatamente após a leitura do arquivo de entrada e a gravação do arquivo de saída.

Desvios incondicionais

Observando nossa PROCEDURE DIVISION neste momento notamos que o programa faz uma (primeira) leitura do arquivo de entrada, incrementa o contador de registros lidos (que no primeiro momento estará com zerado), depois verifica o status da duplicata, preenche o registro de saída e grava esse registro no arquivo de saída.

Precisamos agora ler o segundo, terceiro, quarto registros e assim sucessivamente até o final do arquivo de entrada.

Como qualquer linguagem o COBOL permite a construção de diversos tipos de loop, e veremos quase todos eles mais adiante quando falarmos sobre programação estruturada.

Por ora, nosso programa seguirá o modelo da programação linear, onde o fluxo de execução segue do primeiro ao último comando, com alguns desvios pelo caminho. A programação linear, na verdade, não apresenta nenhuma vantagem técnica sobre a programa estruturada, mas facilita o entendimento daqueles que estão tendo contato com a linguagem pela primeira vez.

Para ler o próximo registro a ser processado precisamos voltar ao comando de leitura do arquivo CRA0205. Neste momento, faremos esse desvio com o comando GO TO, cuja sintaxe é:

GO TO nome-de-paragrafo

Logo, precisamos inserir um nome de parágrafo antes da leitura e incluir um comando GO TO depois do IF.

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       INICIO-DO-PROGRAMA.

           OPEN INPUT CRA0205 OUTPUT CRA0206.

       LE-ARQUIVO.

           READ CRA0205
           ADD 1 TO WT-CT-LIDOS

           IF CRA0205-ST-DUPLICATA NOT = “CNC”
              MOVE CRA0205-NR-FATURA TO CRA0206-NR-FATURA
              MOVE CRA0205-NR-DUPLICATA TO CRA0206-NR-DUPLICATA
              MOVE CRA0205-CD-CLIENTE TO CRA0206-CD-CLIENTE
              MOVE CRA0205-DT-EMISSAO TO CRA0206-DT-EMISSAO
              MOVE CRA0205-DT-VENCIMENTO TO CRA0206-DT-VENCIMENTO
              MOVE CRA0205-VL-FATURA TO CRA0206-VL-FATURA
              MOVE CRA0205-CD-CATEGORIA TO CRA0206-CD-CATEGORIA
              MOVE CRA0205-ST-DUPLICATA TO CRA0206-ST-DUPLICATA
              WRITE CRA0206-REGISTRO
              ADD 1 TO WT-CT-GRAVADOS
           END-IF

           GO TO LE-ARQUIVO

Repare que, agora, o comando OPEN se tornou o último comando do parágrafo INICIO-DO-PROGRAMA e por isso tivemos que colocar um ponto no final da sentença. LE-ARQUIVO foi escrito a partir da coluna 8 e também termina com um ponto, como é obrigatório na declaração de parágrafos. O GO TO na última linha fecha o loop, fazendo com que o fluxo de execução do programa leia o próximo registro disponível no arquivo CRA0205.

Agora precisamos capturar a situação de fim de arquivo para encerrar a execução do programa de forma normal. Para isso, completaremos o comando READ com a cláusula AT END.

Esta cláusula é usada quase que exclusivamente para acessos sequenciais, onde um registro é lido após o outro, na ordem em que foram gravados. Quando detectarmos a condição de fim de arquivo (AT END) desviaremos o programa para fora do loop, onde codificaremos as instruções finais do programa.

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       INICIO-DO-PROGRAMA.

           OPEN INPUT CRA0205 OUTPUT CRA0206.

       LE-ARQUIVO.

           READ CRA0205 AT END GO TO TERMINA-PROGRAMA.
           ADD 1 TO WT-CT-LIDOS

           IF CRA0205-ST-DUPLICATA NOT = “CNC”
              MOVE CRA0205-NR-FATURA TO CRA0206-NR-FATURA
              MOVE CRA0205-NR-DUPLICATA TO CRA0206-NR-DUPLICATA
              MOVE CRA0205-CD-CLIENTE TO CRA0206-CD-CLIENTE
              MOVE CRA0205-DT-EMISSAO TO CRA0206-DT-EMISSAO
              MOVE CRA0205-DT-VENCIMENTO TO CRA0206-DT-VENCIMENTO
              MOVE CRA0205-VL-FATURA TO CRA0206-VL-FATURA
              MOVE CRA0205-CD-CATEGORIA TO CRA0206-CD-CATEGORIA
              MOVE CRA0205-ST-DUPLICATA TO CRA0206-ST-DUPLICATA
              WRITE CRA0206-REGISTRO
              ADD 1 TO WT-CT-GRAVADOS
           END-IF

           GO TO LE-ARQUIVO.

       TERMINA-PROGRAMA.

Repare, mais uma vez, que agora GO TO LE-ARQUIVO se tornou o último comando do parágrafo LE-ARQUIVO e por isso tivemos que encerrá-lo com um ponto.

O COBOL permite que se execute quaisquer comandos após a cláusula AT END.. No nosso exemplo, porém, fizemos apenas um desvio incondicional para o novo parágrafo TERMINA-PROGRAMA.

Cobol: O comando GO TO

Fechando arquivos

Todos os arquivos abertos precisam ser fechados no final do programa para que os buffers de gravação sejam atualizados e os recursos liberados. O comando COBOL responsável pelo fechamento de arquivos é o CLOSE, que tem o seguinte formato.

CLOSE nome-do-arquivo

O CLOSE permite que diversos arquivos sejam fechados com um único comando:

CLOSE nome-arquivo-1 nome-arquivo-2 ... nome-arquivo-N

O fechamento dos arquivos CRA0205 e CRA0206 será o primeiro comando do parágrafo de encerramento do programa:

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       INICIO-DO-PROGRAMA.

           OPEN INPUT CRA0205 OUTPUT CRA0206.

       LE-ARQUIVO.

           READ CRA0205 AT END GO TO TERMINA-PROGRAMA.
           ADD 1 TO WT-CT-LIDOS

           IF CRA0205-ST-DUPLICATA NOT = “CNC”
              MOVE CRA0205-NR-FATURA TO CRA0206-NR-FATURA
              MOVE CRA0205-NR-DUPLICATA TO CRA0206-NR-DUPLICATA
              MOVE CRA0205-CD-CLIENTE TO CRA0206-CD-CLIENTE
              MOVE CRA0205-DT-EMISSAO TO CRA0206-DT-EMISSAO
              MOVE CRA0205-DT-VENCIMENTO TO CRA0206-DT-VENCIMENTO
              MOVE CRA0205-VL-FATURA TO CRA0206-VL-FATURA
              MOVE CRA0205-CD-CATEGORIA TO CRA0206-CD-CATEGORIA
              MOVE CRA0205-ST-DUPLICATA TO CRA0206-ST-DUPLICATA
              WRITE CRA0206-REGISTRO
              ADD 1 TO WT-CT-GRAVADOS
           END-IF

           GO TO LE-ARQUIVO.

       TERMINA-PROGRAMA.

           CLOSE CRA0205 CRA0206

Exibindo a mensagem final

Logo em seguida, como previsto na especificação, precisamos exibir na tela os contadores de registros lidos e gravados. Para isso usaremos o comando DISPLAY:

DISPLAY texto-a-ser-exibido

O texto a ser exibido pode ser formado por literais e/ou variáveis. Assim inserindo o comando abaixo em nosso programa…

DISPLAY “LIDOS=” WT-CT-LIDOS “ GRAVADOS=” WT-CT-GRAVADOS

…fazemos com que a seguinte mensagem apareça na tela do computador quando o programa estiver prestes a terminar:

LIDOS=aaaaaa GRAVADOS=bbbbbb

…onde aaaaaa será o valor final do contador WT-CT-LIDOS e bbbbbb será o valor final da variável WT-CR-GRAVADOS.

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       INICIO-DO-PROGRAMA.

           OPEN INPUT CRA0205 OUTPUT CRA0206.

       LE-ARQUIVO.

           READ CRA0205 AT END GO TO TERMINA-PROGRAMA.
           ADD 1 TO WT-CT-LIDOS

           IF CRA0205-ST-DUPLICATA NOT = “CNC”
              MOVE CRA0205-NR-FATURA TO CRA0206-NR-FATURA
              MOVE CRA0205-NR-DUPLICATA TO CRA0206-NR-DUPLICATA
              MOVE CRA0205-CD-CLIENTE TO CRA0206-CD-CLIENTE
              MOVE CRA0205-DT-EMISSAO TO CRA0206-DT-EMISSAO
              MOVE CRA0205-DT-VENCIMENTO TO CRA0206-DT-VENCIMENTO
              MOVE CRA0205-VL-FATURA TO CRA0206-VL-FATURA
              MOVE CRA0205-CD-CATEGORIA TO CRA0206-CD-CATEGORIA
              MOVE CRA0205-ST-DUPLICATA TO CRA0206-ST-DUPLICATA
              WRITE CRA0206-REGISTRO
              ADD 1 TO WT-CT-GRAVADOS
           END-IF

           GO TO LE-ARQUIVO.

       TERMINA-PROGRAMA.

           CLOSE CRA0205 CRA0206

           DISPLAY “LIDOS=” WT-CT-LIDOS “ GRAVADOS=” WT-CT-GRAVADOS

Cobol: O comando DISPLAY em modo batch

Encerrando a execução do programa

Neste ponto, já executamos todas as funções que nos foram especificadas. Para encerrar a execução, simplesmente codificados a instrução STOP RUN na última linha do programa. Por ser o último comando do parágrafo TERMINA-PROGRAMA, esse comando também precisa terminar com um ponto final.

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       INICIO-DO-PROGRAMA.

           OPEN INPUT CRA0205 OUTPUT CRA0206.

       LE-ARQUIVO.

           READ CRA0205 AT END GO TO TERMINA-PROGRAMA.
           ADD 1 TO WT-CT-LIDOS

           IF CRA0205-ST-DUPLICATA NOT = “CNC”
              MOVE CRA0205-NR-FATURA TO CRA0206-NR-FATURA
              MOVE CRA0205-NR-DUPLICATA TO CRA0206-NR-DUPLICATA
              MOVE CRA0205-CD-CLIENTE TO CRA0206-CD-CLIENTE
              MOVE CRA0205-DT-EMISSAO TO CRA0206-DT-EMISSAO
              MOVE CRA0205-DT-VENCIMENTO TO CRA0206-DT-VENCIMENTO
              MOVE CRA0205-VL-FATURA TO CRA0206-VL-FATURA
              MOVE CRA0205-CD-CATEGORIA TO CRA0206-CD-CATEGORIA
              MOVE CRA0205-ST-DUPLICATA TO CRA0206-ST-DUPLICATA
              WRITE CRA0206-REGISTRO
              ADD 1 TO WT-CT-GRAVADOS
           END-IF

           GO TO LE-ARQUIVO.

       TERMINA-PROGRAMA.

           CLOSE CRA0205 CRA0206

           DISPLAY “LIDOS=” WT-CT-LIDOS “ GRAVADOS=” WT-CT-GRAVADOS

           STOP RUN.

O comando STOP RUN possui alguns recursos muito utilizados em sistemas batch. Por exemplo, você pode encerrar a execução de um programa passando um código de retorno para o sistema operacional. Você pode salvar esse código de retorno numa variável da WORKING-STORAGE e depois mencioná-la no próprio comando STOP RUN

STOP RUN RETURNING variável.

O código de retorno é útil para interferir na execução do job ou script que chamou o programa. Suponha que um job execute cinco programas, e que cada programa só possa ser executado se o anterior terminar com sucesso. Você pode codificar seus programas com uma variável de retorno que assumirá os valores zero para execuções bem sucedidas e qualquer outro valor diferente de zero quando detectar uma anormalidade que exija a interrupção do job.

O código de retorno é testado de formas diferentes por cada sistema operacional. No Windows você pode testá-lo após o programa usando a variável %ERRORLEVEL%. No Unix ou Linux, usando shells como bash, sh ou ksh, é possível testar a variável $?. No mainframe, o valor retornado pelo STOP RUN fica na variável RC, que pode ser testada pelo JCL.

Se você não quiser declarar uma variável na WORKING-STORAGE só para isso, você pode usar uma variável do próprio sistema chamada RETURN-CODE. Basta mover algum valor numérico para ela e encerrar o programa com STOP RUN sem a opção RETURNING.

Conclusão

O programa CRP0206 está completo e pronto para ser compilado:

      *================================================================*
       IDENTIFICATION DIVISION.
      *----------------------------------------------------------------*
       PROGRAM-ID.    CRP0206.
       AUTHOR.        PAULO ANDRE DIAS.
       DATE-WRITTEN.  23/12/2016.
       REMARKS.
      *----------------------------------------------------------------*
      * SISTEMA:      CR - CONTAS A RECEBER
      * JOB:          02 - GERACAO DE FLUXO DE CAIXA
      * PROGRAMA:     06 - SELECIONA DUPLICATAS ATIVAS
      *
      * OBJETIVO:     GERAR ARQUIVO DE SAIDA QUE CONTEM APENAS AS DU-
      *               PLICATAS QUE NAO FORAM CANCELADAS (SITUACAO DA
      *               DUPLICATA DEVE SER DIFERENTE DE "CNC")
      *
      * VERSOES:      DATA    DESCRICAO
      *               ------  ---------------------------------------
      *               XXXXXX  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      *
      *----------------------------------------------------------------*

      *================================================================*
       ENVIRONMENT DIVISION.
      *----------------------------------------------------------------*
       CONFIGURATION SECTION.
       SPECIAL-NAMES.
           DECIMAL-POINT IS COMMA.

       INPUT-OUTPUT SECTION.
       FILE-CONTROL.

      *----------------------------------------------------------------*
      * UNLOAD DA TABELA DE DUPLICATAS
      *----------------------------------------------------------------*
           SELECT CRA0205 ASSIGN TO "$DAT/CRA0205.DAT"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WT-ST-CRA0205.

      *----------------------------------------------------------------*
      * DUPLICATAS ATIVIDADES EMITIDAS NO PERIODO
      *----------------------------------------------------------------*
           SELECT CRA0206 ASSIGN TO "$DAT/CRA0206.DAT"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WT-ST-CRA0206.

      *================================================================*
       DATA DIVISION.
      *----------------------------------------------------------------*
       FILE SECTION.

      *----------------------------------------------------------------*
      * UNLOAD DA TABELA DE DUPLICATAS
      *----------------------------------------------------------------*
       FD CRA0205.
       01 CRA0205-REGISTRO.
           03 CRA0205-NR-FATURA        PIC  9(006).
           03 CRA0205-NR-DUPLICATA     PIC  9(002).
           03 CRA0205-CD-CLIENTE       PIC  X(006).
           03 CRA0205-DT-EMISSAO       PIC  9(008).
           03 CRA0205-DT-VENCIMENTO    PIC  9(008).
           03 CRA0205-VL-FATURA        PIC S9(013)V9(002).
           03 CRA0205-CD-CATEGORIA     PIC  X(003).
           03 CRA0205-ST-DUPLICATA     PIC  X(003).

      *----------------------------------------------------------------*
      * DUPLICATAS ATIVIDADES EMITIDAS NO PERIODO
      *----------------------------------------------------------------*
       FD CRA0206.
       01 CRA0206-REGISTRO.
           03 CRA0206-NR-FATURA        PIC  9(006).
           03 CRA0206-NR-DUPLICATA     PIC  9(002).
           03 CRA0206-CD-CLIENTE       PIC  X(006).
           03 CRA0206-DT-EMISSAO       PIC  9(008).
           03 CRA0206-DT-VENCIMENTO    PIC  9(008).
           03 CRA0206-VL-FATURA        PIC S9(013)V9(002).
           03 CRA0206-CD-CATEGORIA     PIC  X(003).
           03 CRA0206-ST-DUPLICATA     PIC  X(003).

      *================================================================*
       WORKING-STORAGE SECTION.
      *----------------------------------------------------------------*
       01 WT-FILE-STATUS.
           03 WT-ST-CRA0205            PIC  X(002) VALUE SPACES.
           03 WT-ST-CRA0206            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.

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       ABRE-ARQUIVOS.

           OPEN INPUT CRA0205 OUTPUT CRA0206.

       LE-ARQUIVO.

           READ CRA0205 AT END GO TO TERMINA-PROGRAMA.
           ADD 1 TO WT-CT-LIDOS

           IF CRA0205-ST-DUPLICATA NOT = "CNC"
               MOVE CRA0205-NR-FATURA TO CRA0206-NR-FATURA
               MOVE CRA0205-NR-DUPLICATA TO CRA0206-NR-DUPLICATA
               MOVE CRA0205-CD-CLIENTE TO CRA0206-CD-CLIENTE
               MOVE CRA0205-DT-EMISSAO TO CRA0206-DT-EMISSAO
               MOVE CRA0205-DT-VENCIMENTO TO CRA0206-DT-VENCIMENTO
               MOVE CRA0205-VL-FATURA TO CRA0206-VL-FATURA
               MOVE CRA0205-CD-CATEGORIA TO CRA0206-CD-CATEGORIA
               MOVE CRA0205-ST-DUPLICATA TO CRA0206-ST-DUPLICATA
               WRITE CRA0206-REGISTRO
               ADD 1 TO WT-CT-GRAVADOS
           END-IF

           GO TO LE-ARQUIVO.

       TERMINA-PROGRAMA.

           CLOSE CRA0205 CRA0206

           DISPLAY "LIDOS=" WT-CT-LIDOS " GRAVADOS " WT-CT-GRAVADOS

           STOP RUN.

Nos próximos capítulos vamos reestruturar esse programa para que ele não tenha desvios incondicionais e construiremos mais alguns programas com muitos outros comandos e recursos do COBOL.


Anterior Conteúdo Próxima