11. Ordenando arquivos
Muitas vezes precisamos que nossos arquivos de entrada sejam lidos numa ordem pré-definida. Esse recurso é necessário, por exemplo, quando precisamos totalizar valores agrupados por determinadas chaves ou quando precisamos comparar dois arquivos. Neste capítulo veremos não só como reordenar arquivos sequenciais usando comandos do COBOL, mas também quatro de suas principais aplicações: quebra, agrupamento, merge e balance-line.
É muito comum, principalmente em mainframes, que se use um utilitário externo para classificar arquivos, entregando seus registros já ordenados para o programa COBOL. Neste caso, o COBOL não faz nenhuma operação especial de classificação. Neste capítulo, porém, veremos uma modalidade conhecida como sort interno, onde os registros são lidos, classificados e processados por um único programa, escrito em COBOL.
Conceitos sobre a classificação de arquivos
O COBOL possui um comando específico para classificação de arquivos sequenciais, chamado SORT. Porém, para que esse comando seja executado são necessárias três etapas:
Na primeira etapa, chamada fase de release ou fase de input, o programa COBOL lê os registros do arquivo de entrada e entrega esses registros para o processo de classificação. A segunda etapa, chamada simplesmente de sort, é o próprio processo de reordenamento dos registros, orientada por parâmetros fornecidos ao comando SORT. Nesse comando precisamos informar quais são as chaves de classificação e indicar se essas chaves serão classificadas em ordem ascendente ou descendente. A terceira etapa, chamada de fase de return ou fase de output, é quando o programa COBOL solicita ao SORT que devolva os registros reordenados para que ele possa processá-los. Na etapa de release, o programa não precisa enviar todos os registros lidos para a classificação. Na verdade isso depende da finalidade do programa… algumas vezes pode ser necessário enviar para classificação apenas alguns registros do arquivo de entrada, seguindo algum critério de seleção coerente com a funcionalidade do programa.
Classificando todos os registros de um arquivo
Para executar o comando SORT precisamos declarar um arquivo temporário onde colocaremos os registros (na fase de release), para que eles sejam classificados (na fase de sort) e posteriormente recuperados (na fase de return). Esse arquivo temporário é declarado na ENVIRONMENT DIVISION como qualquer outro arquivo:
SELECT nome-do-arquivo ASSIGN TO “nome/externo/do/arquivo” ORGANIZATION IS LINE SEQUENTIAL.
Normalmente esse arquivo segue a mesma organização do arquivo de entrada. Logo, se nosso arquivo de entrada for sequencial linear, precisamos deixar isso claro no comando SELECT, como fizemos nesse exemplo acima.
Também como qualquer arquivo, o arquivo temporário precisa ser detalhado na FILE SECTION da DATA DIVISION. Só que nesse caso não usaremos um parágrafo de file description (FD) e sim um parágrafo específico para sort description, ou SD:
*================================================================*
DATA DIVISION.
*----------------------------------------------------------------*
FILE SECTION.
SD nome-do-arquivo-temporário.
01 nome-do-registro-temporário.
03 nome-do-campo-1 PIC picture-do-campo-1.
03 nome-do-campo-2 PIC picture-do-campo-2.
03 nome-do-campo-3 PIC picture-do-campo-3.
03 nome-do-campo-N PIC picture-do-campo-N.
As regras para definição de nomes de registro, nomes de campo e pictures são as mesmas que usamos até aqui. O arquivo temporário é deletado pelo COBOL assim que o comando SORT terminar a classificação. Esse comando tem a seguinte sintaxe:
SORT nome-do-arquivo-temporário ON ASCENDING KEY nome-do-campo-1 USING nome-do-arquivo-entrada GIVING nome-do-arquivo-saida
Esse é o formato mais simples do comando. Basicamente ele pegará todos os registros do arquivo de entrada, classificará esses registros pelo campo informado e gravará os registros ordenados no arquivo de saída.
Podemos ordenar o arquivo por mais de um campo, incluindo seus nomes na cláusula ON ASCENDING KEY :
SORT nome-do-arquivo-temporário ON ASCENDING KEY nome-do-campo-1, nome-do-campo-2 USING nome-do-arquivo-entrada GIVING nome-do-arquivo-saida
Esse comando classificaria os registros primeiro pelo campo nome-do-campo-1, e nos casos em que os valores desse campo fossem iguais os registros seriam ordenados por nome-do-campo-2.
Podemos ordenar os arquivos em ordem descendente de um ou mais campos, substituindo ON ASCENDING KEY por ON DESCENDING KEY ou mesmo combinando os dois:
SORT nome-do-arquivo-temporário ON ASCENDING KEY nome-do-campo-1 ON DESCEDING KEY nome-do-campo-2 USING nome-do-arquivo-entrada GIVING nome-do-arquivo-saida
Vamos aplicar esses conceitos num programa real bem simples, cujo único objetivo é classificar nosso velho conhecido CRA0207V gerando um arquivo chamado CRA0209 onde todos os registros estarão ordenados por código do cliente, número da fatura e número da duplicata. Desta vez não usaremos books apenas para ficar evidente que os três arquivos são iguais:
*================================================================* IDENTIFICATION DIVISION. *----------------------------------------------------------------* PROGRAM-ID. CRP0209. AUTHOR. PAULO ANDRE DIAS. DATE-WRITTEN. 09/01/2017. REMARKS. *----------------------------------------------------------------* * SISTEMA: CR - CONTAS A RECEBER * JOB: 02 - GERACAO DE FLUXO DE CAIXA * PROGRAMA: 09 - CLASSIFICA DUPLICATAS * * OBJETIVO: ESSE PROGRAMA CLASSIFICA O ARQUIVO DE DUPLICATAS * VALIDAS EM ORDEM DE CLIENTE, FATURA E DUPLICATA * * VERSOES: DATA DESCRICAO * ------ --------------------------------------- * XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *----------------------------------------------------------------* *================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * ARQUIVO DE ENTRADA SELECT CRA0207V ASSIGN TO "$DAT/CRA0207V.DAT" ORGANIZATION IS LINE SEQUENTIAL. * ARQUIVO TEMPORARIO SELECT TMP0207V ASSIGN TO "$DAT/TMP0208.SRT" ORGANIZATION IS LINE SEQUENTIAL. * ARQUIVO DE SAIDA SELECT CRA0209 ASSIGN TO "$DAT/CRA0209.DAT" ORGANIZATION IS LINE SEQUENTIAL. *================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * ARQUIVO DE ENTRADA FD CRA0207V. 01 CRA0207V-REGISTRO. 03 CRA0207V-NR-FATURA PIC 9(006). 03 CRA0207V-NR-DUPLICATA PIC 9(002). 03 CRA0207V-CD-CLIENTE PIC X(006). 03 CRA0207V-DT-EMISSAO PIC 9(008). 03 CRA0207V-DT-VENCIMENTO PIC 9(008). 03 CRA0207V-VL-FATURA PIC S9(013)V9(002). 03 CRA0207V-CD-CATEGORIA PIC X(003). 03 CRA0207V-ST-DUPLICATA PIC X(003). * ARQUIVO TEMPORARIO SD TMP0207V. 01 TMP0207V-REGISTRO. 03 TMP0207V-NR-FATURA PIC 9(006). 03 TMP0207V-NR-DUPLICATA PIC 9(002). 03 TMP0207V-CD-CLIENTE PIC X(006). 03 TMP0207V-DT-EMISSAO PIC 9(008). 03 TMP0207V-DT-VENCIMENTO PIC 9(008). 03 TMP0207V-VL-FATURA PIC S9(013)V9(002). 03 TMP0207V-CD-CATEGORIA PIC X(003). 03 TMP0207V-ST-DUPLICATA PIC X(003). * ARQUIVO DE SAIDA FD CRA0209. 01 CRA0209-REGISTRO. 03 CRA0209-NR-FATURA PIC 9(006). 03 CRA0209-NR-DUPLICATA PIC 9(002). 03 CRA0209-CD-CLIENTE PIC X(006). 03 CRA0209-DT-EMISSAO PIC 9(008). 03 CRA0209-DT-VENCIMENTO PIC 9(008). 03 CRA0209-VL-FATURA PIC S9(013)V9(002). 03 CRA0209-CD-CATEGORIA PIC X(003). 03 CRA0209-ST-DUPLICATA PIC X(003). *================================================================* PROCEDURE DIVISION. *----------------------------------------------------------------* 0-PRINCIPAL. SORT TMP0207V ON ASCENDING KEY TMP0207V-CD-CLIENTE TMP0207V-NR-FATURA TMP0207V-NR-DUPLICATA USING CRA0207V GIVING CRA0209 STOP RUN.
Repare que os três arquivos são declarados na ENVIRONMENT exatamente da mesma forma. A única diferença está na FILE SECTION, onde o detalhamento do arquivo temporário é feito num parágrafo SD.
Nossa PROCEDURE só tem o comando para classificação. O arquivo temporário não precisa ser aberto nem fechado. Na verdade, como este programa só faz o SORT, nem precisamos abrir ou fechar os arquivos de entrada e saída.
Para mostrar o resultado da classificação, vamos modificar ligeiramente a PROCEDURE DIVISION para exibir na tela os registros do arquivo de entrada e do arquivo de saída, logo após o SORT.
*================================================================* PROCEDURE DIVISION. *----------------------------------------------------------------* 0-PRINCIPAL. SORT TMP0207V ON ASCENDING KEY TMP0207V-CD-CLIENTE TMP0207V-NR-FATURA TMP0207V-NR-DUPLICATA USING CRA0207V GIVING CRA0209 DISPLAY " " DISPLAY "*** REGISTROS NO ARQUIVO DE ENTRADA" OPEN INPUT CRA0207V READ CRA0207V PERFORM UNTIL WT-ST-CRA0207V NOT = "00" DISPLAY "CLI=" CRA0207V-CD-CLIENTE " " "FAT=" CRA0207V-NR-FATURA " " "DUP=" CRA0207V-NR-DUPLICATA " " "VAL=" CRA0207V-VL-FATURA READ CRA0207V END-PERFORM CLOSE CRA0207V DISPLAY " " DISPLAY "*** REGISTROS NO ARQUIVO DE SAIDA" OPEN INPUT CRA0209 READ CRA0209 PERFORM UNTIL WT-ST-CRA0209 NOT = "00" DISPLAY "CLI=" CRA0209-CD-CLIENTE " " "FAT=" CRA0209-NR-FATURA " " "DUP=" CRA0209-NR-DUPLICATA " " "VAL=" CRA0209-VL-FATURA READ CRA0209 END-PERFORM CLOSE CRA0209 STOP RUN.
O trecho em azul abre, lê e fecha o arquivo de entrada, mostrando na tela os campos código do cliente, número da fatura, número da duplicata e valor da duplicata. O trecho em vermelho faz o mesmo com o arquivo de saída, que foi gerado pelo SORT.
O resultado da execução do programa você vê abaixo:
*** REGISTROS NO ARQUIVO DE ENTRADA CLI=000015 FAT=001324 DUP=01 VAL=+0000000000123,45 CLI=000010 FAT=001325 DUP=01 VAL=+0000000002319,00 CLI=000009 FAT=001326 DUP=01 VAL=+0000000000590,00 CLI=000009 FAT=001326 DUP=02 VAL=+0000000000590,00 CLI=000009 FAT=001326 DUP=03 VAL=+0000000000590,00 CLI=000009 FAT=001326 DUP=04 VAL=+0000000000590,00 CLI=000009 FAT=001326 DUP=05 VAL=+0000000000590,00 CLI=000010 FAT=001327 DUP=01 VAL=+0000000004850,00 CLI=000011 FAT=001328 DUP=01 VAL=+0000000001810,15 CLI=000008 FAT=001329 DUP=01 VAL=+0000000005650,00 CLI=000005 FAT=001330 DUP=01 VAL=+0000000000045,00 CLI=000004 FAT=001331 DUP=01 VAL=+0000000002100,00 CLI=000003 FAT=001332 DUP=01 VAL=+0000000000510,00 CLI=000003 FAT=001333 DUP=01 VAL=+0000000002740,00 CLI=000003 FAT=001333 DUP=02 VAL=+0000000002740,00 *** REGISTROS NO ARQUIVO DE SAIDA CLI=000003 FAT=001332 DUP=01 VAL=+0000000000510,00 CLI=000003 FAT=001333 DUP=01 VAL=+0000000002740,00 CLI=000003 FAT=001333 DUP=02 VAL=+0000000002740,00 CLI=000004 FAT=001331 DUP=01 VAL=+0000000002100,00 CLI=000005 FAT=001330 DUP=01 VAL=+0000000000045,00 CLI=000008 FAT=001329 DUP=01 VAL=+0000000005650,00 CLI=000009 FAT=001326 DUP=01 VAL=+0000000000590,00 CLI=000009 FAT=001326 DUP=02 VAL=+0000000000590,00 CLI=000009 FAT=001326 DUP=03 VAL=+0000000000590,00 CLI=000009 FAT=001326 DUP=04 VAL=+0000000000590,00 CLI=000009 FAT=001326 DUP=05 VAL=+0000000000590,00 CLI=000010 FAT=001325 DUP=01 VAL=+0000000002319,00 CLI=000010 FAT=001327 DUP=01 VAL=+0000000004850,00 CLI=000011 FAT=001328 DUP=01 VAL=+0000000001810,15 CLI=000015 FAT=001324 DUP=01 VAL=+0000000000123,45
Repare que o arquivo de saída está classificado primeiro por código do cliente, depois por número da fatura e, por último, pelo número da duplicata.
Classificando apenas alguns registros
O comando SORT que vimos no exemplo anterior fez todo o trabalho de leitura, classificação e geração do arquivo de saída. Mas existem situações em que precisamos processar os registros de entrada antes da classificação (fase de release) e/ou trabalhar os registros classificados para gerar o arquivo de saída (fase de return).
Quando você estiver construindo um programa que precisa classificar milhões de registros (algo muito comum em grandes sistemas) você precisará se preocupar com seu tempo de processamento. E os dois fatores que mais afetam a performance de um processo de classificação são (1) a quantidade de registros classificados e (2) o tamanho de cada registro classificado.
É basicamente para isso que existe outro formato do comando SORT:
SORT arquivo-temporário ON [ASCENDING/DESCENDING] KEY campo1 campo2 ... campoN INPUT PROCEDURE IS parágrafo1 OUTPUT PROCEDURE IS parágrafo2
O uso de INPUT e OUTPUT PROCEDURE nos permite construir a lógica que o COBOL seguirá nas fases de release e return, respectivamente. Nesse caso, teremos que gravar os registros no arquivo temporário antes da classificação (na INPUT PROCEDURE) e depois ler os registros classificados do arquivo temporário para gerar o arquivo de saída (na OUTPUT PROCEDURE).
Na INPUT PROCEDURE, para gravar registros no arquivo temporário usamos o comando RELEASE, ao invés do WRITE:
RELEASE registro-do-arquivo-temporário
O comando RELEASE também permite que usemos a cláusula FROM para indicar uma área de trabalho (normalmente criada na WORKING) cujo conteúdo deve ser copiado para o registro antes de gravá-lo:
RELEASE registro-do-arquivo-temporário FROM área-de-trabalho
Na OUTPUT PROCEDURE, para ler os registros classificados, usamos o comando RETURN, ao invés do READ. Como não temos file status para o arquivo temporário precisamos da cláusula AT END para capturar a situação de fim do arquivo.
RETURN arquivo-temporário AT END Comando1 Comando2 ComandoN END-RETURN
Vamos ver como isso tudo funciona construindo um novo programa, chamado CRP0210. Esse programa vai ler todos os registros de duplicatas válidas do arquivo CRA0207V e selecionar apenas as que estão vencidas (campo situação igual a “VNC”). Em seguida vai classificar os registros selecionados por código de categoria e gravar um arquivo de saída que contém apenas os campos código de categoria e valor da duplicata.
Até a PROCEDURE DIVISION existem poucas diferenças em relação aos programas anteriores:
*================================================================* IDENTIFICATION DIVISION. *----------------------------------------------------------------* PROGRAM-ID. CRP0210. AUTHOR. PAULO ANDRE DIAS. DATE-WRITTEN. 10/01/2017. REMARKS. *----------------------------------------------------------------* * SISTEMA: CR - CONTAS A RECEBER * JOB: 02 - GERACAO DE FLUXO DE CAIXA * PROGRAMA: 10 - GERA DUPLICATAS A RECEBER POR CATEGORIA * * OBJETIVO: ESSE PROGRAMA LE O ARQUIVO DE DUPLICATAS VALIDAS * SELECIONA AS DUPLICATAS VENCIDAS (SITUACAO IGUAL * A "VNC"), CLASSIFICA AS DUPLICATAS SELECIONADAS * POR CODIGO DE CATEGORIA E GERA UM ARQUIVO DE * SAIDA COM CODIGO DE CATEGORIA E VALOR DA DUPLI- * CATA * * VERSOES: DATA DESCRICAO * ------ --------------------------------------- * XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *----------------------------------------------------------------* *================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * DUPLICATAS VALIDAS SELECT CRA0207V ASSIGN TO "$DAT/CRA0207V.DAT" ORGANIZATION IS LINE SEQUENTIAL FILE STATUS IS WT-ST-CRA0207V. * ARQUIVO TEMPORARIO PARA SORT SELECT TMP0210 ASSIGN TO "$DAT/TMP0210.SRT" ORGANIZATION IS LINE SEQUENTIAL FILE STATUS IS WT-ST-TMP0210. * VALORES DEVIDOS POR CATEGORIA SELECT CRA0210 ASSIGN TO "$DAT/CRA0210.DAT" ORGANIZATION IS LINE SEQUENTIAL FILE STATUS IS WT-ST-CRA0210. *================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * DUPLICATAS VALIDAS FD CRA0207V. 01 CRA0207V-REGISTRO. 03 CRA0207V-NR-FATURA PIC 9(006). 03 CRA0207V-NR-DUPLICATA PIC 9(002). 03 CRA0207V-CD-CLIENTE PIC X(006). 03 CRA0207V-DT-EMISSAO PIC 9(008). 03 CRA0207V-DT-VENCIMENTO PIC 9(008). 03 CRA0207V-VL-FATURA PIC S9(013)V9(002). 03 CRA0207V-CD-CATEGORIA PIC X(003). 03 CRA0207V-ST-DUPLICATA PIC X(003). * ARQUIVO TEMPORARIO PARA SORT SD TMP0210. 01 TMP0210-REGISTRO. 03 TMP0210-NR-FATURA PIC 9(006). 03 TMP0210-NR-DUPLICATA PIC 9(002). 03 TMP0210-CD-CLIENTE PIC X(006). 03 TMP0210-DT-EMISSAO PIC 9(008). 03 TMP0210-DT-VENCIMENTO PIC 9(008). 03 TMP0210-VL-FATURA PIC S9(013)V9(002). 03 TMP0210-CD-CATEGORIA PIC X(003). 03 TMP0210-ST-DUPLICATA PIC X(003). * VALORES DEVIDOS POR CATEGORIA FD CRA0210. 01 CRA0210-REGISTRO. 03 CRA0210-CD-CATEGORIA PIC X(003). 03 CRA0210-VL-FATURA PIC S9(013)V9(002). *----------------------------------------------------------------* WORKING-STORAGE SECTION. *----------------------------------------------------------------* 01 WT-FILE-STATUS. 03 WT-ST-CRA0207V PIC X(002) VALUE SPACES. 03 WT-ST-CRA0210 PIC X(002) VALUE SPACES. 03 WT-ST-TMP0210 PIC X(002) VALUE SPACES.
Repare que, dessa vez, nosso arquivo de saída (CRA0210) tem um layout diferente do arquivo de entrada e do arquivo temporário para classificação. O programa seria mais eficiente se o arquivo temporário tivesse apenas os campos necessários para gerar o arquivo de saída, mas deixamos assim para ter o que mostrar na OUTPUT PROCEDURE, mais adiante.
A PROCEDURE DIVISION começa com o comando SORT, desta vez indicando quais serão os parágrafos para INPUT e OUTPUT PROCEDURE.
*================================================================* PROCEDURE DIVISION. *----------------------------------------------------------------* 0-PRINCIPAL. SORT TMP0210 ON ASCENDING KEY TMP0210-CD-CATEGORIA INPUT PROCEDURE IS 1-PROCESSA-ENTRADA OUTPUT PROCEDURE IS 2-PROCESSA-SAIDA STOP RUN.
Esses parágrafos são iguais a todos os outros que construímos até agora. Vejamos como ficou a INPUT PROCEDURE:
*----------------------------------------------------------------*
* LE TODOS OS REGISTROS DO ARQUIVO DE ENTRADA E SELECIONA APENAS
* AQUELES QUE ESTAO COM CODIGO DE SITUACAO IGUAL A "VNC" (VENCI-
* DAS). OS REGISTROS SELECIONADOS SERAO GRAVADOS NO ARQUIVO TEM-
* PORARIO PARA CLASSIFICACAO
*----------------------------------------------------------------*
1-PROCESSA-ENTRADA.
OPEN INPUT CRA0207V
READ CRA0207V
PERFORM 11-SELECIONA-REGISTROS
UNTIL WT-ST-CRA0207V NOT = "00"
CLOSE CRA0207V.
Ele abre o arquivo de entrada e faz a leitura do primeiro registro. Em seguida, executa um parágrafo chamado 11-SELECIONA-REGISTROS até que não haja mais registros para ler. O último comando fecha o arquivo de entrada pois ele não será mais necessário no programa. O SORT só começa a classificar os registros depois do último comando da INPUT PROCEDURE.
O parágrafo 11-SELECIONA-REGISTROS grava no arquivo temporário apenas as duplicatas cuja situação seja igual a “VNC”, como pede a especificação.
*----------------------------------------------------------------*
* SELECIONA REGISTROS PARA CLASSIFICACAO
*----------------------------------------------------------------*
11-SELECIONA-REGISTROS.
IF CRA0207V-ST-DUPLICATA = "VNC"
RELEASE TMP0210-REGISTRO FROM CRA0207V-REGISTRO
END-IF
READ CRA0207V.
Para gravar o registro no arquivo temporário usamos o comando RELEASE com a cláusula FROM, uma vez que o layout do arquivo TMP0210 é igual ao layout do arquivo de entrada, CRA0207V.
Agora vejamos como ficou a OUTPUT PROCEDURE, que é onde vamos gerar nosso arquivo de saída. Esse parágrafo só começa a ser executado depois que o SORT termina de fazer a classificação::
*----------------------------------------------------------------*
* LE TODOS OS REGISTROS CLASSIFICADOS E GRAVA ARQUIVO DE SAIDA
*----------------------------------------------------------------*
2-PROCESSA-SAIDA.
OPEN OUTPUT CRA0210
RETURN TMP0210
PERFORM 21-GRAVA-ARQUIVO-SAIDA
UNTIL WT-ST-TMP0210 NOT = “00”
CLOSE CRA0210.
A leitura dos registros do arquivo temporário é executada com o comando RETURN. O parágrafo 21-GRAVA-ARQUIVO-SAIDA é executado até que não haja mais registros no arquivo temporário de classificação:
*----------------------------------------------------------------* * LE REGISTROS CLASSIFICADOS E GRAVA NO ARQUIVO DE SAIDA *----------------------------------------------------------------* 21-GRAVA-ARQUIVO-SAIDA. MOVE TMP0210-CD-CATEGORIA TO CRA0210-CD-CATEGORIA MOVE TMP0210-VL-FATURA TO CRA0210-VL-FATURA WRITE CRA0210-REGISTRO RETURN TMP0210.
Nesse parágrafo movimentamos para o registro de saída apenas os campos que ele possui: código de categoria e valor da fatura.
Você pode combinar as opções do comando SORT de acordo com a necessidade do programa: pode usar USING e GIVING, USING e OUTPUT PROCEDURE, INPUT PROCEDURE e GIVING ou INPUT PROCEDURE e OUTPUT PROCEDURE. Por exemplo:
SORT TMP0210 ON ASCENDING KEY TMP0210-CD-CATEGORIA INPUT PROCEDURE IS 1-SELECIONA-REGISTROS GIVING CRA0210
Nesse caso definimos uma INPUT PROCEDURE para selecionar os registros que queremos e gravamos num arquivo temporário que tem o mesmo layout do arquivo de saída. Depois da classificação não precisamos de nenhum tratamento adicional, por isso usamos a opção GIVING para copiar o arquivo temporário para o arquivo de saída definitivo.
Outro exemplo:
SORT TMP0210 ON ASCENDING KEY TMP0210-CD-CATEGORIA USING CRA0207V OUTPUT PROCEDURE IS 2-GRAVA-SAIDA
Nesse formato, o arquivo temporário deve ter o mesmo layout do arquivo de entrada e nada será selecionado antes da classificação, por isso a opção USING.
Merge
Merge é o nome que se dá ao processo de juntar dois ou mais arquivos sequenciais que têm o mesmo layout, que foram previamente ordenados pelas mesmas chaves, mas que têm, obviamente, conteúdos diferentes.
É possível usar o comando SORT para juntar arquivos diferentes e produzir um arquivo único, ordenado.
Vejamos um exemplo prático. Vamos imaginar quatro arquivos de duplicatas, um para cada trimestre do ano, que precisamos juntar num único arquivo ordenado por número da fatura e número da duplicata.
*================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * ARQUIVO DO PRIMEIRO TRIMESTRE FD ARQUIVO01. 01 ARQUIVO01-REGISTRO PIC X(051). * ARQUIVO DO SEGUNDO TRIMESTRE FD ARQUIVO02. 01 ARQUIVO02-REGISTRO PIC X(051). * ARQUIVO DO TERCEIRO TRIMESTRE FD ARQUIVO03. 01 ARQUIVO03-REGISTRO PIC X(051). * ARQUIVO DO QUARTO TRIMESTRE FD ARQUIVO04. 01 ARQUIVO04-REGISTRO PIC X(051). * ARQUIVO TEMPORARIO PARA MERGE SD TMPMERGE. 01 TMPMERGE-REGISTRO. 03 TMPMERGE-NR-FATURA PIC 9(006). 03 TMPMERGE-NR-DUPLICATA PIC 9(002). 03 TMPMERGE-CD-CLIENTE PIC X(006). 03 TMPMERGE-DT-EMISSAO PIC 9(008). 03 TMPMERGE-DT-VENCIMENTO PIC 9(008). 03 TMPMERGE-VL-FATURA PIC S9(013)V9(002). 03 TMPMERGE-CD-CATEGORIA PIC X(003). 03 TMPMERGE-ST-DUPLICATA PIC X(003). * ARQUIVO UNICO FD ARQUIVO99. 01 ARQUIVO99-REGISTRO PIC X(051). O comando abaixo faria todo o trabalho: SORT TMPMERGE ON ASCENDING KEY TMPMERGE-NR-FATURA TMPMERGE-NR-DUPLICATA USING ARQUIVO01 ARQUIVO02 ARQUIVO03 ARQUIVO04 GIVING ARQUIVO99
Se estivéssemos trabalhando com arquivos com layouts diferentes, poderíamos criar uma INPUT PROCEDURE que lesse cada um dos arquivos e gravasse um arquivo temporário com o layout unificado. Em seguida a própria opção GIVING geraria o arquivo definitivo de saída, que estaria unificado e ordenado.
Curiosamente o COBOL possui um comando específico para MERGE que é pouquíssimo utilizado. Sua sintaxe é:
MERGE arquivo-temporário ON ASCENDING KEY campo1 campo2 ... campoN USING arquivo1 arquivo2 ... arquivo GIVING arquivo-saida
Repare que ele é praticamente igual ao comando SORT. No programa anterior, poderíamos ter codificado o merge assim:
MERGE TMPMERGE ON ASCENDING KEY TMPMERGE-NR-FATURA TMPMERGE-NR-DUPLICATA USING ARQUIVO01 ARQUIVO02 ARQUIVO03 ARQUIVO04 GIVING ARQUIVO99
Você encontrará muitos programas usando o comando SORT e raramente verá um com o comando MERGE. Existe um motivo para isso: o MERGE espera que todos os arquivos de entrada já estejam ordenados pelas mesmas chaves. Logo, um ou mais SORTs seriam necessários antes do MERGE. E como o próprio SORT faz tanto a classificação quanto a unificação, então não temos necessidade desse comando.
Quebra de um nível
A classificação de arquivos sequenciais é a base de diversas técnicas de programação que usamos frequentemente em ambientes corporativos. Uma dessas técnicas é conhecida pelo nome de “quebra”.
A quebra é utilizada sempre que precisamos separar ou sumarizar um conjunto de informações em função de um ou mais campos de controle.
Imagine um arquivo sequencial chamado FAA0500.DAT que contém a relação de vendas realizadas num período. Em COBOL, seu layout poderia ser assim:
01 FTA0101-REGISTRO. 03 FTA0101-NR-VENDEDOR PIC 9(006). 03 FTA0101-NR-CLIENTE PIC 9(006). 03 FTA0101-NR-FATURA PIC 9(006). 03 FTA0101-DT-VENDA PIC 9(008). 03 FTA0101-VL-VENDA PIC S9(013)V9(002).
Esse arquivo tem o seguinte conteúdo:
Agora suponha que precisemos imprimir um relatório totalizando as vendas por vendedor, como no modelo abaixo:
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
Observe que precisamos mostrar o total de vendas por vendedor, e que o número do vendedor só deve aparecer na sua primeira venda, não nas demais.
Para fazer isso, teremos que ordenar o arquivo de vendas por número de vendedor e depois “quebrar” o relatório por esse número. O número do vendedor, na prática, é o campo de controle que mencionamos anteriormente. Quando o programa precisa de apenas um campo de controle (que é o caso desse exemplo) dizemos que é uma quebra de um nível.
Existem algoritmos padrões para codificar um programa estruturado com quebra de um nível. Podemos ver o diagrama dele na figura abaixo:
O parágrafo principal executará o parágrafo 1 onde estará o comando para classificação do arquivo de entrada. Nesse exemplo veremos que é possível usar o mesmo arquivo como entrada e saída do SORT.
Também no parágrafo 1 ficarão os comandos para abertura de arquivo e primeira leitura do arquivo de entrada, já ordenado. O parágrafo 3 fecha os arquivos de entrada e saída.
O parágrafo 2 será executado em loop, até o fim do arquivo de entrada. Neste parágrafo vamos salvar o campo de controle do arquivo de entrada (número do vendedor) numa variável de trabalho e zerar o total de vendas por vendedor. Em seguida, executaremos o parágrafo 21 até que o número do vendedor do arquivo de entrada seja diferente da variável de trabalho OU até o fim do arquivo de entrada, o que acontecer primeiro. Por último, chamaremos o parágrafo 22 para imprimir os totais por vendedor.
Sempre que o limite de linhas por página for atingido chamaremos o parágrafo X1 para impressão do cabeçalho.
Nosso programa terá uma IDENTIFICATION DIVISION padrão:
*================================================================* 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 * *----------------------------------------------------------------*
Na ENVIRONMENT, além de declarar o arquivo de entrada (FAA0500) e o relatório (FAL0501), temos também o arquivo temporário que usaremos para classificar o arquivo de entrada:
*================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * VENDAS MENSAIS COPY FAS0500. * 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.
Neste programa, o comando SORT usará o próprio arquivo FAA0500 como entrada e saída, ou seja, o SORT vai ler todos os registros na fase de release, fará a classificação por número do vendedor, e gravará os registros ordenados no mesmo arquivo que usou como entrada. Por isso, ao detalhar o arquivo temporário na FILE SECTION só precisamos mencionar a chave de classificação (NR-VENDEDOR). Também precisamos completar seu registro com um FILLER X(035) para que ele fique com o mesmo tamanho registro de entrada.
*================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * VENDAS MENSAIS COPY FAF0500. * 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).
A WORKING começa com uma constante para estabelecer a quantidade de linhas por página, o file status do arquivo de entrada, os contadores de páginas e linhas impressas, um acumulador para o total de vendas por vendedor, variáveis para receber data e hora do sistema e algumas variáveis auxiliares para chamada da subrotina de formatação de datas.
*================================================================* 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. 01 WT-CONTADORES. 03 WT-CT-LIDOS PIC 9(006) VALUE ZEROS. 03 WT-CT-PAGINA PIC 9(003) VALUE ZEROS. 03 WT-CT-LINHAS PIC 9(002) VALUE 99. 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.
Toda vez que o programa ler um registro de entrada que tenha um número de vendedor diferente do número de vendedor do registro anterior, teremos que imprimir o total de vendas do vendedor anterior antes de continuar a impressão do relatório. Precisamos, portanto, guardar o número do vendedor anterior para fazer essa comparação. Para isso, criamos uma variável de trabalho adicional na WORKING:
01 WT-CAMPOS-CONTROLE.
03 WT-CTL-NR-VENDEDOR PIC 9(006) VALUE ZEROS.
Em seguida detalhamos o relatório. Sua estrutura é praticamente igual ao relatório que vimos anteriormente, com uma única diferença: repare que o campo WR-DET-NR-VENDEDOR tem uma cláusula nova, chamada BLANK WHEN ZEROS. Essa opção faz com que uma variável numérica não seja exibida quando estiver zerada. Usaremos esse recurso para fazer com que o número do vendedor só apareça na primeira linha de suas vendas.
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(013) VALUE "CLIENTE". 03 FILLER PIC X(034) 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 FILLER PIC X(007) VALUE SPACES. 03 WR-DET-NR-FATURA PIC 9(006) VALUE ZEROS. 03 FILLER PIC X(023) 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.
O diagrama estruturado mostra que esse programa deve ser organizado em três parágrafos principais. 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-FAA0500 NOT = "00" PERFORM 3-TERMINA STOP RUN.
No parágrafo 1-INICIA ficará a classificação do arquivo de entrada. Nenhum arquivo pode estar aberto no momento da classificação, por isso colocamos o comando SORT antes da abertura dos arquivos. Repare que ele usa o arquivo FAA0500 tanto como entrada (USING) quanto como saída (GIVING):
*----------------------------------------------------------------* * 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 OUTPUT FAL0501 READ FAA0500.
É no parágrafo 2-PROCESSA que acontecerá o controle da quebra de vendedor. Observe que a primeira coisa que ele faz é salvar o número do vendedor que acabou de ser lido naquela variável de trabalho que criamos na WORKING.
Logo em seguida ele move esse mesmo número de vendedor para o campo correspondente no relatório. Essa movimentação só acontecerá nesse ponto do programa, pois queremos que o número do vendedor só apareça na primeira linha de suas vendas. Além disso, como esse parágrafo só é executado quando o programa encontra um novo vendedor, aqui também é o lugar perfeito para zerar o total de vendas por vendedor.
*----------------------------------------------------------------* * 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
Agora vamos chamar o parágrafo que será responsável por imprimir as vendas. Esse parágrafo será executado até que não existam mais registros no arquivo de entrada ou que ele leia um registro do arquivo de entrada onde o número do vendedor seja diferente do que salvamos na variável de controle.
*----------------------------------------------------------------* * 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
Quando o fluxo de execução voltar para o parágrafo 2-PROCESSA (ou porque acabaram os registros ou porque detectou-se a quebra de número de vendedor), precisaremos imprimir o total de vendas do último vendedor processado:
*----------------------------------------------------------------*
* 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.
Vamos codificar o parágrafo final para que os parágrafos apareçam no programa na mesma ordem em que são mencionados pelos PERFORMs. O comando CANCEL está aqui porque teremos que chamar o subprograma XXREDT4 para formatar as datas da linha de detalhe:
*----------------------------------------------------------------* * FECHA ARQUIVOS E EXIBE OS CONTADORES DE REGISTROS LIDOS E IM- * PRESSOS *----------------------------------------------------------------* 3-TERMINA. CANCEL WT-NM-EDT4 CLOSE FAA0500 FAL0501.
O parágrafo 21-IMPRIME-VENDAS começa verificando se a quantidade de linhas impressas está maior que o limite de linhas por página. No início da execução a quantidade de linhas impressas está com um valor maior que esse limite para que o cabeçalho seja impresso na primeira página:
*----------------------------------------------------------------*
* IMPRIME TODAS AS VENDAS DO VENDEDOR
*----------------------------------------------------------------*
21-IMPRIME-VENDAS.
IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA
PERFORM X1-IMPRIME-CABECALHO
END-IF
Todos os campos do arquivo de entrada precisam ser movidos para os campos correspondentes na linha detalhe. Observe que o número do vendedor não é movimentado, porque já fizemos isso no parágrafo 2-PROCESSA. Aqui também chamamos a subrotina XXREDT4 para transformar uma data numérica AAAAMMDD em alfanumérica DD/MM/AAAA:
*----------------------------------------------------------------* * 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-CD-CLIENTE MOVE FAA0500-NR-FATURA TO WR-DET-NR-FATURA MOVE FAA0500-VL-VENDA TO WR-DET-VL-VENDA
Para encerrar esse parágrafo, imprimimos a linha detalhe, incrementamos o contador de linhas impressas, acumulamos o total de vendas por vendedor e lemos o próximo registro do arquivo de entrada.
Mas além disso, precisamos também zerar o número do vendedor na linha detalhe. É isso que vai garantir que a próxima linha de vendas desse vendedor não mostrará seu número. Como a variável WR-DET-NR-VENDEDOR foi declarada na WORKING com BLANK WHEN ZEROS, se ela estiver zerada esse número será exibido como brancos:
*----------------------------------------------------------------*
* 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-CD-CLIENTE
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.
O próximo parágrafo é aquele que imprime o total de vendas por vendedor. Ele também verifica a quantidade de linhas impressas para saber se precisa saltar uma página e imprimir novo cabeçalho. Em seguida, simplesmente movimenta o acumulador de vendas por vendedor para o rodapé e imprime as linhas correspondentes.
Repare que ao imprimir a última linha do rodapé estamos usando uma opção nova, BEFORE 2. Ela faz com que a linha WR-ROD2 seja impressa antes do programa saltar duas linhas. Na prática, ela coloca uma linha em branco entre WR-ROD2 e a próxima linha de vendas, como estava previsto no layout do relatório. Por causa dessa linha em branco, precisamos somar 3 ao contador de linhas impressas.
*----------------------------------------------------------------* * 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.
O último parágrafo do programa é aquele que vai imprimir o cabeçalho no topo de cada página. Seu nome tem o prefixo X1- porque ele é chamado tanto por 21-IMPRIME-VENDAS quanto por 22-IMPRIME-TOTAL.
*----------------------------------------------------------------* * 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.
A quebra pressupõe que o arquivo de entrada esteja ordenado pelo(s) campo(s) de controle e por isso usamos o comando SORT no início do programa. No dia a dia, porém, é mais comum que essa classificação seja feita por um utilitário externo que é codificado um step antes da execução do programa, no mesmo job ou script.
Quebra de múltiplos níveis
O algoritmo para quebra de um nível que vimos no exemplo anterior pode ser facilmente expandido para contemplar quebras de múltiplos níveis, em processos que precisam de mais de um campo de controle.
Vamos acompanhar outro exemplo, desta vez usando dois campos de controle para gerar um relatório com quebra de dois níveis.
Considere o arquivo abaixo, que se chama FPA0513.DAT:
Seu layout já está detalhado no book FPF0513.CPY:
*----------------------------------------------------------------* * SALARIOS *----------------------------------------------------------------* FD FPA0513. 01 FPA0513-REGISTRO. 03 FPA0513-CD-DIRETORIA PIC X(003). 03 FPA0513-CD-DEPARTAMENTO PIC X(003). 03 FPA0513-MT-FUNCIONARIO PIC 9(006). 03 FPA0513-VL-SALARIO PIC S9(013)V9(002).
E suponha que pretendemos gerar o seguinte relatório…
EMPRESA XYZ DATA: 16/01/17 FOLHA PAGTO HORA: 09:59:56 FPL0514 SALARIOS POR DIRETORIA E DEPARTAMENTO PAGINA: 1 -------------------------------------------------------------------------------- DIRETORIA DEPARTAMENTO FUNCIONARIO SALARIO -------------------------------------------------------------------------------- ENS ACD 055314 17.910,00 073415 8.230,00 081873 8.244,00 ------------ TOTAL DO DEPARTAMENTO: 34.384,00 SEC 097341 2.560,00 ------------ TOTAL DO DEPARTAMENTO: 2.560,00 -------------------------------------------------------------------------------- TOTAL DA DIRETORIA: 36.944,00 -------------------------------------------------------------------------------- NEG MKT 056321 17.734,00 109314 3.645,00 ------------ TOTAL DO DEPARTAMENTO: 21.379,00 -------------------------------------------------------------------------------- TOTAL DA DIRETORIA: 21.379,00 -------------------------------------------------------------------------------- OPE ADM 064321 17.408,00 103445 3.200,00 109346 1.005,00 ------------ TOTAL DO DEPARTAMENTO: 21.613,00 FIN 065663 17.410,00 101230 5.600,00 ------------ TOTAL DO DEPARTAMENTO: 23.010,00 INF 090816 11.250,00 081715 12.750,00 099920 7.300,00 ------------ TOTAL DO DEPARTAMENTO: 31.300,00 -------------------------------------------------------------------------------- TOTAL DA DIRETORIA: 75.923,00 --------------------------------------------------------------------------------
Observe que, desta vez, queremos totalizar os valores por dois campos de controle: código da diretoria e código do departamento, e que esses códigos são mostrados apenas nas primeiras linhas em que aparecem.
O algoritmo estruturado que vamos usar é muito semelhante ao que usamos no exemplo anterior. Ele tem um loop a mais de controle justamente para fazer o segundo nível de quebra:
O parágrafo 2-PROCESSA será executado até o final do arquivo de entrada. Ele salvará o código da diretoria, processará todos os salários dessa diretoria e imprimirá o total.
O Parágrafo 21-PROCESSA-DIRETORIA será executado até o fim do arquivo de entrada OU até que seja lido um registro com código de diretoria diferente do anterior. Ele salvará o código do departamento, processará todos os salários desse departamento e imprimirá o total.
O parágrafo 211-PROCESSA-DEPARTAMENTO vai imprimir e acumular os salários em duas variáveis: uma será usada para totalizar os salários por departamento, a outra para totalizar os salários por diretoria.
Para quebrar o relatório por código de diretoria e código de departamento precisamos classificar o arquivo de entrada por esses dois campos. O detalhamento do arquivo temporário na FILE SECTION só precisa desses dois campos. O FILLER PIC X(021) garante que os registros do arquivo temporário terão os mesmos tamanhos dos registros de entrada:
*================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * SALARIOS COPY FPF0513. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO SD SRT0514. 01 SRT0514-REGISTRO. 03 SRT0514-CD-DIRETORIA PIC X(003). 03 SRT0514-CD-DEPARTAMENTO PIC X(003). 03 FILLER PIC X(021). * RELATORIO DE SALARIOS POR DIRETORIA E DEPARTAMENTO FD FPL0514. 01 FPL0514-REGISTRO PIC X(080).
Ao declarar as variáveis de trabalho, precisaremos de duas variáveis de controle e dois acumuladores:
*================================================================* WORKING-STORAGE SECTION. *----------------------------------------------------------------* 01 WC-CONSTANTES. 03 WC-LINHAS-POR-PAGINA PIC 9(002) VALUE 60. 01 WT-FILE-STATUS. 03 WT-ST-FPA0513 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-DIRETORIA PIC S9(013)V9(002) VALUE ZEROS. 03 WT-AC-DEPARTAMENTO PIC S9(013)V9(002) 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 WT-CAMPOS-CONTROLE. 03 WT-CTL-CD-DIRETORIA PIC X(003) VALUE SPACES. 03 WT-CTL-CD-DEPARTAMENTO PIC X(003) VALUE SPACES.
Na definição do relatório precisamos definir as duas linhas de total que aparecem no layout:
01 WR-ROD2. 03 FILLER PIC X(045) VALUE SPACES. 03 FILLER PIC X(023) VALUE "TOTAL DO DEPARTAMENTO: ". 03 WR-ROD-AC-DEPARTAMENTO PIC Z.ZZZ.ZZ9,99 VALUE ZEROS. 01 WR-ROD3. 03 FILLER PIC X(048) VALUE SPACES. 03 FILLER PIC X(020) VALUE "TOTAL DA DIRETORIA: ". 03 WR-ROD-AC-DIRETORIA PIC Z.ZZZ.ZZ9,99 VALUE ZEROS.
A PROCEDURE começa seguindo o modelo padrão. Como no exemplo anterior, a classificação do arquivo de entrada acontece no parágrafo 1-INICIA, antes da abertura dos arquivos. Mas desta vez o comando SORT ordena o arquivo pelos dois campos de controle:
*================================================================* PROCEDURE DIVISION. *----------------------------------------------------------------* 0-PRINCIPAL. PERFORM 1-INICIA PERFORM 2-PROCESSA UNTIL WT-ST-FPA0513 NOT = "00" PERFORM 3-TERMINA STOP RUN. *----------------------------------------------------------------* * CLASSIFICA O ARQUIVO DE ENTRADA POR CODIGO DA DIRETORIA E CODI- * GO DO DEPARTAMENTO. EM SEGUIDA, ABRE OS ARQUIVOS E FAZ A PRI- * MEIRA LEITURA DO ARQUIVO DE ENTRADA *----------------------------------------------------------------* 1-INICIA. SORT SRT0514 ON ASCENDING KEY SRT0514-CD-DIRETORIA SRT0514-CD-DEPARTAMENTO USING FPA0513 GIVING FPA0513 OPEN INPUT FPA0513 OPEN OUTPUT FPL0514 READ FPA0513.
O parágrafo 2-PROCESSA salva o código da diretoria e processa todos os salários dessa diretoria até o fim do arquivo ou a leitura de um registro cujo código da diretoria seja diferente do código anterior. Esse código é movido nesse momento para a linha de detalhe pois só queremos que ele apareça uma vez. Depois que todos os salários de uma diretoria são impressos, o programa chama o parágrafo 22-IMPRIME-TOTAL-DIRETORIA.
*----------------------------------------------------------------* * SALVA O CODIGO DA DIRETORIA E IMPRIME TODOS OS DEPARTAMENTOS * DESSA DIRETORIA. EM SEGUIDA IMPRIME O TOTAL DE SALARIOS DA * DIRETORIA *----------------------------------------------------------------* 2-PROCESSA. MOVE FPA0513-CD-DIRETORIA TO WT-CTL-CD-DIRETORIA MOVE FPA0513-CD-DIRETORIA TO WR-DET-CD-DIRETORIA MOVE ZEROS TO WT-AC-DIRETORIA PERFORM 21-PROCESSA-DIRETORIA UNTIL WT-ST-FPA0513 NOT = "00" OR FPA0513-CD-DIRETORIA NOT = WT-CTL-CD-DIRETORIA PERFORM 22-IMPRIME-TOTAL-DIRETORIA.
O parágrafo 21-PROCESSA-DIRETORIA será responsável por controlar o segundo nível de quebra. Ele salvará o código do departamento e moverá esse código para a linha de detalhe nesse momento pois também só queremos que ele apareça uma vez. Logo em seguida executará o parágrafo que lista os salários do departamento até que: (1) não haja mais registros no arquivo de entrada, ou (2) leia um código de diretoria diferente do código anterior, ou (3) leia um código de departamento diferente do código anterior. Se uma dessas condições acontecer o programa imprimirá o total de salários por departamento:
*----------------------------------------------------------------* * SALVA O CODIGO DO DEPARTAMENTO E IMPRIME TODOS OS SALARIOS DES- * SE DEPARTAMENTO. EM SEGUIDA, IMPRIME O TOTAL DE SALARIOS DO * DEPARTAMENTO *----------------------------------------------------------------* 21-PROCESSA-DIRETORIA. MOVE FPA0513-CD-DEPARTAMENTO TO WT-CTL-CD-DEPARTAMENTO MOVE FPA0513-CD-DEPARTAMENTO TO WR-DET-CD-DEPARTAMENTO MOVE ZEROS TO WT-AC-DEPARTAMENTO PERFORM 211-PROCESSA-DEPARTAMENTO UNTIL WT-ST-FPA0513 NOT = "00" OR FPA0513-CD-DIRETORIA NOT = WT-CTL-CD-DIRETORIA OR FPA0513-CD-DEPARTAMENTO NOT = WT-CTL-CD-DEPARTAMENTO PERFORM 212-IMPRIME-TOTAL-DEPARTAMENTO.
Em seguida precisamos codificar o parágrafo que imprime o total de salários por diretoria…
*----------------------------------------------------------------* * IMPRIME TOTAL DE SALARIOS DA DIRETORIA *----------------------------------------------------------------* 22-IMPRIME-TOTAL-DIRETORIA. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF MOVE WT-AC-DIRETORIA TO WR-ROD-AC-DIRETORIA WRITE FPL0514-REGISTRO FROM WR-SEP1 WRITE FPL0514-REGISTRO FROM WR-ROD3 WRITE FPL0514-REGISTRO FROM WR-SEP1 BEFORE 2 ADD 4 TO WT-CT-LINHAS.
A impressão das linhas de detalhe acontece no parágrafo 211-PROCESSA-DEPARTAMENTO. O salário que foi impresso é somado a dois acumuladores porque teremos duas linhas de totalização no relatório. Depois que uma linha é impressa, movemos espaços para o código da diretoria e para o código de departamento porque só queremos que eles apareçam uma vez.
*----------------------------------------------------------------* * IMPRIME SALARIOS DOS FUNCIONARIOS DE DETERMINADO DEPARTAMENTO *----------------------------------------------------------------* 211-PROCESSA-DEPARTAMENTO. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF MOVE FPA0513-MT-FUNCIONARIO TO WR-DET-MT-FUNCIONARIO MOVE FPA0513-VL-SALARIO TO WR-DET-VL-SALARIO WRITE FPL0514-REGISTRO FROM WR-DET1 ADD 1 TO WT-CT-LINHAS ADD FPA0513-VL-SALARIO TO WT-AC-DIRETORIA ADD FPA0513-VL-SALARIO TO WT-AC-DEPARTAMENTO MOVE SPACES TO WR-DET-CD-DIRETORIA MOVE SPACES TO WR-DET-CD-DEPARTAMENTO READ FPA0513.
Os últimos parágrafos completam o programa e serão usados para imprimir o total de salários por departamento e o cabeçalho no topo de cada página, sem diferenças em relação ao programa do exemplo anterior:
*----------------------------------------------------------------* * IMPRIME TOTAL DE SALARIOS DO DEPARTAMENTO *----------------------------------------------------------------* 212-IMPRIME-TOTAL-DEPARTAMENTO. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF MOVE WT-AC-DEPARTAMENTO TO WR-ROD-AC-DEPARTAMENTO WRITE FPL0514-REGISTRO FROM WR-ROD1 WRITE FPL0514-REGISTRO FROM WR-ROD2 BEFORE 2 ADD 3 TO WT-CT-LINHAS.
Resumindo, cada nível de quebra precisa de um parágrafo que:
- Salve o campo de controle numa variável de trabalho
- Zere o contador ou acumulador desse campo de controle, se houver
- Execute um comando PERFORM até o fim do arquivo de entrada ou até que ocorra uma quebra do campo de controle atual ou dos campos de controle anteriores
- Imprima, se houver, contadores ou acumuladores do campo de controle que acabou de quebrar
Seguindo essa estrutura, podemos construir programas que façam quebras em três, quatro, cinco… ou quantos níveis forem necessários. O programa FPP0514 completo é mostrado abaixo:
*================================================================* IDENTIFICATION DIVISION. *----------------------------------------------------------------* PROGRAM-ID. FPP0514. AUTHOR. PAULO ANDRE DIAS. DATE-WRITTEN. 16/01/2017. REMARKS. *----------------------------------------------------------------* * SISTEMA: FP - FOLHA DE PAGAMENTO * JOB: 05 - RELATORIOS * PROGRAMA: 14 - SALARIOS POR DEPARTAMENTO * * OBJETIVO: LER O ARQUIVO DE SALARIOS E GERAR RELATORIO TO- * TALIZANDO POR DIRETORIA E DEPARTAMENTO * * VERSOES: DATA DESCRICAO * ------ --------------------------------------- * XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *----------------------------------------------------------------* *================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * SALARIOS COPY FPS0513. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO SELECT SRT0514 ASSIGN TO "$DAT/SRT0514.DAT" ORGANIZATION IS LINE SEQUENTIAL. * RELATORIO DE SALARIOS POR DIRETORIA E DEPARTAMENTO SELECT FPL0514 ASSIGN TO "$DAT/FPL0514.TXT" ORGANIZATION IS LINE SEQUENTIAL. *================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * SALARIOS COPY FPF0513. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO SD SRT0514. 01 SRT0514-REGISTRO. 03 SRT0514-CD-DIRETORIA PIC X(003). 03 SRT0514-CD-DEPARTAMENTO PIC X(003). 03 FILLER PIC X(021). * RELATORIO DE SALARIOS POR DIRETORIA E DEPARTAMENTO FD FPL0514. 01 FPL0514-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-FPA0513 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-DIRETORIA PIC S9(013)V9(002) VALUE ZEROS. 03 WT-AC-DEPARTAMENTO PIC S9(013)V9(002) 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 WT-CAMPOS-CONTROLE. 03 WT-CTL-CD-DIRETORIA PIC X(003) VALUE SPACES. 03 WT-CTL-CD-DEPARTAMENTO PIC X(003) VALUE SPACES. 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(021) VALUE "FPL0514". 03 FILLER PIC X(045) VALUE "SALARIOS POR DIRETORIA E DEPARTAMENTO". 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 "DIRETORIA". 03 FILLER PIC X(018) VALUE "DEPARTAMENTO". 03 FILLER PIC X(040) VALUE "FUNCIONARIO". 03 FILLER PIC X(007) VALUE "SALARIO". 01 WR-DET1. 03 WR-DET-CD-DIRETORIA PIC X(003) VALUE SPACES. 03 FILLER PIC X(012) VALUE SPACES. 03 WR-DET-CD-DEPARTAMENTO PIC X(003) VALUE SPACES. 03 FILLER PIC X(015) VALUE SPACES. 03 WR-DET-MT-FUNCIONARIO PIC 9(006) VALUE ZEROS. 03 FILLER PIC X(031) VALUE SPACES. 03 WR-DET-VL-SALARIO 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(045) VALUE SPACES. 03 FILLER PIC X(023) VALUE "TOTAL DO DEPARTAMENTO: ". 03 WR-ROD-AC-DEPARTAMENTO PIC Z.ZZZ.ZZ9,99 VALUE ZEROS. 01 WR-ROD3. 03 FILLER PIC X(048) VALUE SPACES. 03 FILLER PIC X(020) VALUE "TOTAL DA DIRETORIA: ". 03 WR-ROD-AC-DIRETORIA PIC Z.ZZZ.ZZ9,99 VALUE ZEROS. *================================================================* PROCEDURE DIVISION. *----------------------------------------------------------------* 0-PRINCIPAL. PERFORM 1-INICIA PERFORM 2-PROCESSA UNTIL WT-ST-FPA0513 NOT = "00" PERFORM 3-TERMINA STOP RUN. *----------------------------------------------------------------* * CLASSIFICA O ARQUIVO DE ENTRADA POR CODIGO DA DIRETORIA E CODI- * GO DO DEPARTAMENTO. EM SEGUIDA, ABRE OS ARQUIVOS E FAZ A PRI- * MEIRA LEITURA DO ARQUIVO DE ENTRADA *----------------------------------------------------------------* 1-INICIA. SORT SRT0514 ON ASCENDING KEY SRT0514-CD-DIRETORIA SRT0514-CD-DEPARTAMENTO USING FPA0513 GIVING FPA0513 OPEN INPUT FPA0513 OPEN OUTPUT FPL0514 READ FPA0513. *----------------------------------------------------------------* * SALVA O CODIGO DA DIRETORIA E IMPRIME TODOS OS DEPARTAMENTOS * DESSA DIRETORIA. EM SEGUIDA IMPRIME O TOTAL DE SALARIOS DA * DIRETORIA *----------------------------------------------------------------* 2-PROCESSA. MOVE FPA0513-CD-DIRETORIA TO WT-CTL-CD-DIRETORIA MOVE FPA0513-CD-DIRETORIA TO WR-DET-CD-DIRETORIA MOVE ZEROS TO WT-AC-DIRETORIA PERFORM 21-PROCESSA-DIRETORIA UNTIL WT-ST-FPA0513 NOT = "00" OR FPA0513-CD-DIRETORIA NOT = WT-CTL-CD-DIRETORIA PERFORM 22-IMPRIME-TOTAL-DIRETORIA. *----------------------------------------------------------------* * FECHA ARQUIVOS E ZERA O RETURN-CODE *----------------------------------------------------------------* 3-TERMINA. CLOSE FPA0513 FPL0514 MOVE ZEROS TO RETURN-CODE. *----------------------------------------------------------------* * SALVA O CODIGO DO DEPARTAMENTO E IMPRIME TODOS OS SALARIOS DES- * SE DEPARTAMENTO. EM SEGUIDA, IMPRIME O TOTAL DE SALARIOS DO * DEPARTAMENTO *----------------------------------------------------------------* 21-PROCESSA-DIRETORIA. MOVE FPA0513-CD-DEPARTAMENTO TO WT-CTL-CD-DEPARTAMENTO MOVE FPA0513-CD-DEPARTAMENTO TO WR-DET-CD-DEPARTAMENTO MOVE ZEROS TO WT-AC-DEPARTAMENTO PERFORM 211-PROCESSA-DEPARTAMENTO UNTIL WT-ST-FPA0513 NOT = "00" OR FPA0513-CD-DIRETORIA NOT = WT-CTL-CD-DIRETORIA OR FPA0513-CD-DEPARTAMENTO NOT = WT-CTL-CD-DEPARTAMENTO PERFORM 212-IMPRIME-TOTAL-DEPARTAMENTO. *----------------------------------------------------------------* * IMPRIME TOTAL DE SALARIOS DA DIRETORIA *----------------------------------------------------------------* 22-IMPRIME-TOTAL-DIRETORIA. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF MOVE WT-AC-DIRETORIA TO WR-ROD-AC-DIRETORIA WRITE FPL0514-REGISTRO FROM WR-SEP1 WRITE FPL0514-REGISTRO FROM WR-ROD3 WRITE FPL0514-REGISTRO FROM WR-SEP1 BEFORE 2 ADD 4 TO WT-CT-LINHAS. *----------------------------------------------------------------* * IMPRIME SALARIOS DOS FUNCIONARIOS DE DETERMINADO DEPARTAMENTO *----------------------------------------------------------------* 211-PROCESSA-DEPARTAMENTO. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF MOVE FPA0513-MT-FUNCIONARIO TO WR-DET-MT-FUNCIONARIO MOVE FPA0513-VL-SALARIO TO WR-DET-VL-SALARIO WRITE FPL0514-REGISTRO FROM WR-DET1 ADD 1 TO WT-CT-LINHAS ADD FPA0513-VL-SALARIO TO WT-AC-DIRETORIA ADD FPA0513-VL-SALARIO TO WT-AC-DEPARTAMENTO MOVE SPACES TO WR-DET-CD-DIRETORIA MOVE SPACES TO WR-DET-CD-DEPARTAMENTO READ FPA0513. *----------------------------------------------------------------* * IMPRIME TOTAL DE SALARIOS DO DEPARTAMENTO *----------------------------------------------------------------* 212-IMPRIME-TOTAL-DEPARTAMENTO. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF MOVE WT-AC-DEPARTAMENTO TO WR-ROD-AC-DEPARTAMENTO WRITE FPL0514-REGISTRO FROM WR-ROD1 WRITE FPL0514-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 FPL0514-REGISTRO FROM WR-CAB1 AFTER PAGE WRITE FPL0514-REGISTRO FROM WR-CAB2 WRITE FPL0514-REGISTRO FROM WR-CAB3 WRITE FPL0514-REGISTRO FROM WR-SEP1 WRITE FPL0514-REGISTRO FROM WR-CAB4 WRITE FPL0514-REGISTRO FROM WR-SEP1 MOVE 6 TO WT-CT-LINHAS.
Agrupamento
Os algoritmos de quebra que vimos até aqui nos permitiram exibir informações ordenadas por um ou mais campos de controle. Em muitas situações, porém, não queremos (ou não precisamos) de informações detalhadas, e sim de um sumário dessas informações.
A estrutura de um programa que sumariza ou acumula informações por um ou mais campos de controle é praticamente a mesma que usamos nos programas de quebra.
Para mostrar como o agrupamento funciona, criaremos um programa chamado FPP0515 que inicialmente será uma cópia do nosso último exemplo, FPP0514. Só que dessa vez, queremos um relatório que mostre apenas a média salarial de cada diretoria/departamento:
EMPRESA XYZ DATA: 99/99/99 FOLHA PAGTO HORA: 99:99:99 FPL0514 MEDIA SALARIAL POR DIRETORIA E DEPARTAMENTO PAGINA: ZZ9 -------------------------------------------------------------------------------- DIRETORIA DEPARTAMENTO SALARIO MEDIO -------------------------------------------------------------------------------- XXX XXX ZZZ.ZZ9,99 XXX ZZZ.ZZ9,99 XXX ZZZ.ZZ9,99 ------------ MEDIA SALARIAL DA DIRETORIA: ZZZ.ZZ9,99 XXX XXX ZZZ.ZZ9,99 XXX ZZZ.ZZ9,99 ------------ MEDIA SALARIAL DA DIRETORIA: ZZZ.ZZ9,99 XXX XXX ZZZ.ZZ9,99 XXX ZZZ.ZZ9,99 XXX ZZZ.ZZ9,99 XXX ZZZ.ZZ9,99 XXX ZZZ.ZZ9,99 ------------ MEDIA SALARIAL DA DIRETORIA: ZZZ.ZZ9,99
Depois de copiar do programa, vamos destacar apenas as alterações necessárias para transformá-lo num programa de agrupamento. A primeira coisa que precisamos fazer, naturalmente, é alterar a IDENTIFICATION DIVISION:
*================================================================* IDENTIFICATION DIVISION. *----------------------------------------------------------------* PROGRAM-ID. FPP0515. AUTHOR. PAULO ANDRE DIAS. DATE-WRITTEN. 17/01/2017. REMARKS. *----------------------------------------------------------------* * SISTEMA: FP - FOLHA DE PAGAMENTO * JOB: 05 - RELATORIOS * PROGRAMA: 15 - MEDIA SALARIAL POR DEPARTAMENTO * * OBJETIVO: LER O ARQUIVO DE SALARIOS E GERAR RELATORIO MOS- * TRANDO A MEDIA SALARIAL DE CADA DEPARTAMENTO E * DIRETORIA * * VERSOES: DATA DESCRICAO * ------ --------------------------------------- * XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *----------------------------------------------------------------*
O arquivo de entrada será o mesmo, FPA0513, e sua classificação também será igual, pois os salários serão sumarizados por diretoria e departamento, como no programa anterior. Na ENVIRONMENT e na DATA DIVISION alteraremos apenas o nome do arquivo temporário e o nome do relatório. Faremos isso com um único find & replace, disponível em qualquer editor de programas, substituindo “0514” para “0515”. Assim atualizamos de uma só vez tanto a ENVIRONMENT e a DATA quanto a PROCEDURE DIVISION:
*================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * SALARIOS COPY FPS0513. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO SELECT SRT0515 ASSIGN TO "$DAT/SRT0515.DAT" ORGANIZATION IS LINE SEQUENTIAL. * MEDIA SALARIAL POR DIRETORIA E DEPARTAMENTO SELECT FPL0515 ASSIGN TO "$DAT/FPL0515.TXT" ORGANIZATION IS LINE SEQUENTIAL. *================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * SALARIOS COPY FPF0513. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO SD SRT0515. 01 SRT0515-REGISTRO. 03 SRT0515-CD-DIRETORIA PIC X(003). 03 SRT0515-CD-DEPARTAMENTO PIC X(003). 03 FILLER PIC X(021). * MEDIA SALARIAL POR DIRETORIA E DEPARTAMENTO FD FPL0515. 01 FPL0515-REGISTRO PIC X(080).
O novo programa usará os mesmos acumuladores, só que agora para calcular a média (e não o somatório) dos salários de cada departamento. Nós vamos fazer isso somando os salários de cada registro lido e depois dividindo essa soma pela quantidade de salários lidos até a quebra. Precisamos criar dois novos contadores na WORKING: um para a quantidade de salários de cada diretoria e outro para a quantidade de salários de cada departamento:
01 WT-CONTADORES. 03 WT-CT-PAGINA PIC 9(003) VALUE ZEROS. 03 WT-CT-LINHAS PIC 9(002) VALUE 99. 03 WT-CT-DIRETORIA PIC 9(006) VALUE ZEROS. 03 WT-CT-DEPARTAMENTO PIC 9(006) VALUE ZEROS.
O relatório não precisa mais mostrar o salário de cada funcionário. A linha de detalhe mostrará a média de salários ao lado do código de cada departamento. O código da diretoria só será exibido na primeira linha em que aparecer, e o único rodapé que teremos mostrará agora a média salarial de a cada quebra de diretoria.
Precisamos alterar o título do relatório, obviamente atualizando o tamanho do FILLER anterior para que esse título continue centralizado no cabeçalho:
01 WR-CAB3.
03 FILLER PIC X(018) VALUE
"FPL0515".
03 FILLER PIC X(048) VALUE
"MEDIA SALARIAL POR DIRETORIA E DEPARTAMENTO".
03 FILLER PIC X(011) VALUE
"PAGINA: ".
03 WR-CAB-PAGINA PIC ZZ9 VALUE
ZEROS.
Podemos remover o título da coluna que mostrava a matrícula dos funcionários. Também alteraremos a picture do FILLER anterior para que a média salarial de cada departamento continue na margem direita da página
01 WR-CAB4.
03 FILLER PIC X(015) VALUE "DIRETORIA".
03 FILLER PIC X(052) VALUE "DEPARTAMENTO".
03 FILLER PIC X(040) VALUE "FUNCIONARIO".
03 FILLER PIC X(013) VALUE "SALARIO MEDIO".
Faremos o mesmo na linha de detalhe…
01 WR-DET1. 03 WR-DET-CD-DIRETORIA PIC X(003) VALUE SPACES. 03 FILLER PIC X(012) VALUE SPACES. 03 WR-DET-CD-DEPARTAMENTO PIC X(003) VALUE SPACES. 03 FILLER PIC X(052) VALUE SPACES. 03 WR-DET-MT-FUNCIONARIO PIC 9(006) VALUE ZEROS. 03 FILLER PIC X(031) VALUE SPACES. 03 WR-DET-VL-SALARIO PIC ZZZ.ZZ9,99 VALUE ZEROS.
E atualizamos o rodapé para refletir o novo layout:
01 WR-ROD1. 03 FILLER PIC X(068) VALUE SPACES. 03 FILLER PIC X(012) VALUE ALL "-". 01 WR-ROD2. 03 FILLER PIC X(039) VALUE SPACES. 03 FILLER PIC X(029) VALUE "MEDIA SALARIAL DA DIRETORIA: ". 03 WR-ROD-AC-DIRETORIA PIC Z.ZZZ.ZZ9,99 VALUE ZEROS. 01 WR-ROD3. 03 FILLER PIC X(048) VALUE SPACES. 03 FILLER PIC X(020) VALUE "TOTAL DA DIRETORIA: ". 03 WR-ROD-AC-DIRETORIA PIC Z.ZZZ.ZZ9,99 VALUE ZEROS.
Em 2-PROCESSA vamos apenas alterar a linha de comentários e o nome do parágrafo que antes imprimia o total de salários. Além de zerar o acumulador de salários da diretoria, agora precisamos zerar também o contador de salários:
*----------------------------------------------------------------* * SALVA O CODIGO DA DIRETORIA E IMPRIME TODOS OS DEPARTAMENTOS * DESSA DIRETORIA. EM SEGUIDA IMPRIME A MEDIA SALARIAL DA DI- * RETORIA *----------------------------------------------------------------* 2-PROCESSA. MOVE FPA0513-CD-DIRETORIA TO WT-CTL-CD-DIRETORIA MOVE FPA0513-CD-DIRETORIA TO WR-DET-CD-DIRETORIA MOVE ZEROS TO WT-CT-DIRETORIA MOVE ZEROS TO WT-AC-DIRETORIA PERFORM 21-PROCESSA-DIRETORIA UNTIL WT-ST-FPA0513 NOT = "00" OR FPA0513-CD-DIRETORIA NOT = WT-CTL-CD-DIRETORIA PERFORM 22-IMPRIME-MEDIA-DIRETORIA.
E faremos praticamente a mesma coisa em 21-PROCESSA-DIRETORIA:
*----------------------------------------------------------------* * SALVA O CODIGO DO DEPARTAMENTO, PROCESSA TODOS OS SALARIOS DES- * SE DEPARTAMENTO. EM SEGUIDA, IMPRIME A MEDIA DE SALARIOS DO * DEPARTAMENTO *----------------------------------------------------------------* 21-PROCESSA-DIRETORIA. MOVE FPA0513-CD-DEPARTAMENTO TO WT-CTL-CD-DEPARTAMENTO MOVE FPA0513-CD-DEPARTAMENTO TO WR-DET-CD-DEPARTAMENTO MOVE ZEROS TO WT-CT-DEPARTAMENTO MOVE ZEROS TO WT-AC-DEPARTAMENTO PERFORM 211-PROCESSA-DEPARTAMENTO UNTIL WT-ST-FPA0513 NOT = "00" OR FPA0513-CD-DIRETORIA NOT = WT-CTL-CD-DIRETORIA OR FPA0513-CD-DEPARTAMENTO NOT = WT-CTL-CD-DEPARTAMENTO PERFORM 212-IMPRIME-MEDIA-DEPARTAMENTO.
Como veremos um pouco mais adiante, o parágrafo 211-PROCESSA-DEPARTAMENTO não imprimirá mais nada no relatório. Ele vai apenas acumular os salários lidos nas variáveis WT-AC-DIRETORIA e WT-AC-DEPARTAMENTO e contar os salários lidos nas variáveis WT-CT-DIRETORIA e WT-CT-DEPARTAMENTO. A média salarial, tanto da diretoria quanto do departamento, será calculada no momento em que for impressa. E é isso que faremos agora no parágrafo 22-IMPRIME-MEDIA-DIRETORIA usando o comando DIVIDE. Também precisamos alterar as linhas que serão exibidas no rodapé para refletir o novo layout do relatório:
*----------------------------------------------------------------*
* IMPRIME MEDIA DE SALARIOS DA DIRETORIA
*----------------------------------------------------------------*
22-IMPRIME-MEDIA-DIRETORIA.
IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA
PERFORM X1-IMPRIME-CABECALHO
END-IF
DIVIDE WT-CT-DIRETORIA INTO WT-AC-DIRETORIA
MOVE WT-AC-DIRETORIA TO WR-ROD-AC-DIRETORIA
WRITE FPL0514-REGISTRO FROM WR-SEP1
WRITE FPL0514-REGISTRO FROM WR-ROD3
WRITE FPL0514-REGISTRO FROM WR-SEP1 BEFORE 2
ADD 4 TO WT-CT-LINHAS.
Aqui vale relembrar alguns detalhes sobre operações aritméticas em COBOL. Existem vários formatos possíveis para o comando DIVIDE. Esse que usamos…
DIVIDE variavel1 INTO variavel2
Faz a mesma coisa que…
DIVIDE variavel2 BY variavel1 GIVING variavel2
Ou…
COMPUTE variavel2 = variavel2 / variavel1
Uma dúvida que pode ter surgido nesse momento é: por que simplesmente não usar a variável WR-ROD-AC-DIRETORIA como destino? Algo parecido com…
DIVIDE WT-AC-DIRETORIA BY WT-CT-DIRETORIA GIVING WR-ROD-AC-DIRETORIA
O COBOL não permitiria essa operação porque, como já mencionamos, a variável WR-ROD-AC-DIRETORIA tem picture editada (ZZZ.ZZ9,99), e variáveis com picture editada só podem ser usadas para exibição; não podem ser usadas em operações aritméticas.
Voltando ao programa…
O parágrafo que antes imprimia a linha detalhe agora só vai contar e acumular os salários do departamento. Por causa disso, o parágrafo 211-PROCESSA-DEPARTAMENTO talvez seja o mais modificado do novo programa: eliminamos os comandos de impressão e incluímos os comandos para contar a quantidade de salários:
*----------------------------------------------------------------* * CONTA E ACUMULA SALARIOS DE DETERMINADO DEPARTAMENTO *----------------------------------------------------------------* 211-PROCESSA-DEPARTAMENTO. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF MOVE FPA0513-MT-FUNCIONARIO TO WR-DET-MT-FUNCIONARIO MOVE FPA0513-VL-SALARIO TO WR-DET-VL-SALARIO WRITE FPL0515-REGISTRO FROM WR-DET1 ADD 1 TO WT-CT-LINHAS ADD FPA0513-VL-SALARIO TO WT-AC-DIRETORIA ADD FPA0513-VL-SALARIO TO WT-AC-DEPARTAMENTO ADD 1 TO WT-CT-DIRETORIA ADD 1 TO WT-CT-DEPARTAMENTO MOVE SPACES TO WR-DET-CD-DIRETORIA MOVE SPACES TO WR-DET-CD-DEPARTAMENTO READ FPA0513.
A impressão da linha detalhe agora ficará no parágrafo 22-IMPRIME-MEDIA-DEPARTAMENTO. Calcularemos a média salarial do departamento antes de movê-la para a linha detalhe. Também precisaremos apagar o código da diretoria, porque só queremos que ele apareça na primeira linha em que ele aparecer.
*----------------------------------------------------------------* * IMPRIME MEDIA DE SALARIOS DO DEPARTAMENTO *----------------------------------------------------------------* 212-IMPRIME-MEDIA-DEPARTAMENTO. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF DIVIDE WT-CT-DEPARTAMENTO INTO WT-AC-DEPARTAMENTO MOVE WT-AC-DEPARTAMENTO TO WR-DET-VL-SALARIO WRITE FPL0515-REGISTRO FROM WR-DET1 WRITE FPL0515-REGISTRO FROM WR-ROD2 BEFORE 2 ADD 1 TO WT-CT-LINHAS MOVE SPACES TO WR-DET-CD-DIRETORIA.
E com isso transformamos um programa que imprimia salários em um programa que calcula a média de salários, usando as mesmas variáveis de controle, diretoria e departamento:
EMPRESA XYZ DATA: 17/01/17 FOLHA PAGTO HORA: 08:44:56 FPL0515 MEDIA SALARIAL POR DIRETORIA E DEPARTAMENTO PAGINA: 1 -------------------------------------------------------------------------------- DIRETORIA DEPARTAMENTO SALARIO MEDIO -------------------------------------------------------------------------------- ENS ACD 11.461,33 SEC 2.560,00 ------------ MEDIA SALARIAL DA DIRETORIA: 9.236,00 NEG MKT 10.689,50 ------------ MEDIA SALARIAL DA DIRETORIA: 10.689,50 OPE ADM 7.204,33 FIN 11.505,00 INF 10.433,33 ------------ MEDIA SALARIAL DA DIRETORIA: 9.490,37
Nosso novo programa ficou assim:
*================================================================* IDENTIFICATION DIVISION. *----------------------------------------------------------------* PROGRAM-ID. FPP0515. AUTHOR. PAULO ANDRE DIAS. DATE-WRITTEN. 17/01/2017. REMARKS. *----------------------------------------------------------------* * SISTEMA: FP - FOLHA DE PAGAMENTO * JOB: 05 - RELATORIOS * PROGRAMA: 15 - MEDIA SALARIAL POR DEPARTAMENTO * * OBJETIVO: LER O ARQUIVO DE SALARIOS E GERAR RELATORIO MOS- * TRANDO A MEDIA SALARIAL DE CADA DEPARTAMENTO E * DIRETORIA * * VERSOES: DATA DESCRICAO * ------ --------------------------------------- * XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *----------------------------------------------------------------* *================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * SALARIOS COPY FPS0513. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO SELECT SRT0515 ASSIGN TO "$DAT/SRT0515.DAT" ORGANIZATION IS LINE SEQUENTIAL. * MEDIA SALARIAL POR DIRETORIA E DEPARTAMENTO SELECT FPL0515 ASSIGN TO "$DAT/FPL0515.TXT" ORGANIZATION IS LINE SEQUENTIAL. *================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * SALARIOS COPY FPF0513. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO SD SRT0515. 01 SRT0515-REGISTRO. 03 SRT0515-CD-DIRETORIA PIC X(003). 03 SRT0515-CD-DEPARTAMENTO PIC X(003). 03 FILLER PIC X(021). * MEDIA SALARIAL POR DIRETORIA E DEPARTAMENTO FD FPL0515. 01 FPL0515-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-FPA0513 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-DIRETORIA PIC 9(006) VALUE ZEROS. 03 WT-CT-DEPARTAMENTO PIC 9(006) VALUE ZEROS. 01 WT-ACUMULADORES. 03 WT-AC-DIRETORIA PIC S9(013)V9(002) VALUE ZEROS. 03 WT-AC-DEPARTAMENTO PIC S9(013)V9(002) 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 WT-CAMPOS-CONTROLE. 03 WT-CTL-CD-DIRETORIA PIC X(003) VALUE SPACES. 03 WT-CTL-CD-DEPARTAMENTO PIC X(003) VALUE SPACES. 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(018) VALUE "FPL0515". 03 FILLER PIC X(048) VALUE "MEDIA SALARIAL POR DIRETORIA E DEPARTAMENTO". 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 "DIRETORIA". 03 FILLER PIC X(052) VALUE "DEPARTAMENTO". 03 FILLER PIC X(013) VALUE "SALARIO MEDIO". 01 WR-DET1. 03 WR-DET-CD-DIRETORIA PIC X(003) VALUE SPACES. 03 FILLER PIC X(012) VALUE SPACES. 03 WR-DET-CD-DEPARTAMENTO PIC X(003) VALUE SPACES. 03 FILLER PIC X(052) VALUE SPACES. 03 WR-DET-VL-SALARIO 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(039) VALUE SPACES. 03 FILLER PIC X(029) VALUE "MEDIA SALARIAL DA DIRETORIA: ". 03 WR-ROD-AC-DIRETORIA PIC Z.ZZZ.ZZ9,99 VALUE ZEROS. *================================================================* PROCEDURE DIVISION. *----------------------------------------------------------------* 0-PRINCIPAL. PERFORM 1-INICIA PERFORM 2-PROCESSA UNTIL WT-ST-FPA0513 NOT = "00" PERFORM 3-TERMINA STOP RUN. *----------------------------------------------------------------* * CLASSIFICA O ARQUIVO DE ENTRADA POR CODIGO DA DIRETORIA E CODI- * GO DO DEPARTAMENTO. EM SEGUIDA, ABRE OS ARQUIVOS E FAZ A PRI- * MEIRA LEITURA DO ARQUIVO DE ENTRADA *----------------------------------------------------------------* 1-INICIA. SORT SRT0515 ON ASCENDING KEY SRT0515-CD-DIRETORIA SRT0515-CD-DEPARTAMENTO USING FPA0513 GIVING FPA0513 OPEN INPUT FPA0513 OPEN OUTPUT FPL0515 READ FPA0513. *----------------------------------------------------------------* * SALVA O CODIGO DA DIRETORIA E IMPRIME TODOS OS DEPARTAMENTOS * DESSA DIRETORIA. EM SEGUIDA IMPRIME A MEDIA SALARIAL DA DI- * RETORIA *----------------------------------------------------------------* 2-PROCESSA. MOVE FPA0513-CD-DIRETORIA TO WT-CTL-CD-DIRETORIA MOVE FPA0513-CD-DIRETORIA TO WR-DET-CD-DIRETORIA MOVE ZEROS TO WT-CT-DIRETORIA MOVE ZEROS TO WT-AC-DIRETORIA PERFORM 21-PROCESSA-DIRETORIA UNTIL WT-ST-FPA0513 NOT = "00" OR FPA0513-CD-DIRETORIA NOT = WT-CTL-CD-DIRETORIA PERFORM 22-IMPRIME-MEDIA-DIRETORIA. *----------------------------------------------------------------* * FECHA ARQUIVOS E ZERA O RETURN-CODE *----------------------------------------------------------------* 3-TERMINA. CLOSE FPA0513 FPL0515 MOVE ZEROS TO RETURN-CODE. *----------------------------------------------------------------* * SALVA O CODIGO DO DEPARTAMENTO, PROCESSA TODOS OS SALARIOS DES- * SE DEPARTAMENTO. EM SEGUIDA, IMPRIME A MEDIA DE SALARIOS DO * DEPARTAMENTO *----------------------------------------------------------------* 21-PROCESSA-DIRETORIA. MOVE FPA0513-CD-DEPARTAMENTO TO WT-CTL-CD-DEPARTAMENTO MOVE FPA0513-CD-DEPARTAMENTO TO WR-DET-CD-DEPARTAMENTO MOVE ZEROS TO WT-CT-DEPARTAMENTO MOVE ZEROS TO WT-AC-DEPARTAMENTO PERFORM 211-PROCESSA-DEPARTAMENTO UNTIL WT-ST-FPA0513 NOT = "00" OR FPA0513-CD-DIRETORIA NOT = WT-CTL-CD-DIRETORIA OR FPA0513-CD-DEPARTAMENTO NOT = WT-CTL-CD-DEPARTAMENTO PERFORM 212-IMPRIME-MEDIA-DEPARTAMENTO. *----------------------------------------------------------------* * IMPRIME MEDIA DE SALARIOS DA DIRETORIA *----------------------------------------------------------------* 22-IMPRIME-MEDIA-DIRETORIA. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF DIVIDE WT-CT-DIRETORIA INTO WT-AC-DIRETORIA MOVE WT-AC-DIRETORIA TO WR-ROD-AC-DIRETORIA WRITE FPL0515-REGISTRO FROM WR-ROD1 WRITE FPL0515-REGISTRO FROM WR-ROD2 BEFORE 2 ADD 3 TO WT-CT-LINHAS. *----------------------------------------------------------------* * CONTA E ACUMULA SALARIOS DE DETERMINADO DEPARTAMENTO *----------------------------------------------------------------* 211-PROCESSA-DEPARTAMENTO. ADD FPA0513-VL-SALARIO TO WT-AC-DIRETORIA ADD FPA0513-VL-SALARIO TO WT-AC-DEPARTAMENTO ADD 1 TO WT-CT-DIRETORIA ADD 1 TO WT-CT-DEPARTAMENTO READ FPA0513. *----------------------------------------------------------------* * IMPRIME MEDIA DE SALARIOS DO DEPARTAMENTO *----------------------------------------------------------------* 212-IMPRIME-MEDIA-DEPARTAMENTO. IF WT-CT-LINHAS > WC-LINHAS-POR-PAGINA PERFORM X1-IMPRIME-CABECALHO END-IF DIVIDE WT-CT-DEPARTAMENTO INTO WT-AC-DEPARTAMENTO MOVE WT-AC-DEPARTAMENTO TO WR-DET-VL-SALARIO WRITE FPL0515-REGISTRO FROM WR-DET1 ADD 1 TO WT-CT-LINHAS MOVE SPACES TO WR-DET-CD-DIRETORIA. *----------------------------------------------------------------* * 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 FPL0515-REGISTRO FROM WR-CAB1 AFTER PAGE WRITE FPL0515-REGISTRO FROM WR-CAB2 WRITE FPL0515-REGISTRO FROM WR-CAB3 WRITE FPL0515-REGISTRO FROM WR-SEP1 WRITE FPL0515-REGISTRO FROM WR-CAB4 WRITE FPL0515-REGISTRO FROM WR-SEP1 MOVE 6 TO WT-CT-LINHAS.
Balance-Line
A técnica de balance-line é usada quando precisamos processar dois ou mais arquivos sequenciais interdependentes. Ela é muito comum em grandes sistemas e pode ser empregada em diversas situações, como por exemplo:
- Quando precisamos atualizar um arquivo sequencial com informações existentes em outro arquivo sequencial;
- Quando precisamos comparar os arquivos e tomar decisões sobre registros que existem em um mas não existem em outros;
- Ou ao contrário, quando precisamos comparar os arquivos e tomar decisões sobre registros que existem em todos
A complexidade do algoritmo, naturalmente, aumenta em função do número de arquivos envolvidos. Por esse motivo, é mais comum encontrarmos programas que façam balance-line somente entre dois arquivos.
Quando a quantidade de registros é grande nos dois arquivos, o desempenho de um programa batch fazendo balance-line é significativamente superior a um programa que tente fazer o mesmo processamento usando arquivos de acesso direto, como tabelas em banco de dados ou arquivos indexados.
O algoritmo usado na construção de um balance-line pressupõe que todos os arquivos envolvidos estejam classificados pelas mesmas chaves, formadas por um ou mais campos. Os arquivos são lidos simultaneamente e suas chaves de classificação são comparadas para que se possa tomar alguma decisão quando…
- A chave no primeiro arquivo é menor do que a chave no segundo arquivo, o que significa que a chave existe no primeiro mas não existe no segundo;
- A chave no primeiro arquivo é igual à chave no segundo arquivo, o que significa que a chave existe nos dois arquivos
- A chave no primeiro arquivo é maior do que a chave no segundo arquivo, que significa que a chave existe no segundo mas não existe no primeiro
A melhor forma de entender como isso tudo funciona (e para que serve) é analisando um exemplo prático.
Vamos supor que em nosso sistema exista um arquivo chamado CRA0207V que contém duplicatas que ainda não foram pagas. A chave desse arquivo é composta pelos campos NR-FATURA e NR-DUPLICATA, e seu conteúdo é mostrado na figura abaixo:
O layout desse arquivo está detalhado no book CRF0207V:
FD CRA0207V. 01 CRA0207V-REGISTRO. 03 CRA0207V-CHAVE. 05 CRA0207V-NR-FATURA PIC 9(006). 05 CRA0207V-NR-DUPLICATA PIC 9(002). 03 CRA0207V-CD-CLIENTE PIC X(006). 03 CRA0207V-DT-EMISSAO PIC 9(008). 03 CRA0207V-DT-VENCTO PIC 9(008). 03 CRA0207V-VL-DUPLICATA PIC S9(013)V9(002). 03 CRA0207V-CD-CATEGORIA PIC X(003). 03 CRA0207V-ST-DUPLICATA PIC X(003).
Vamos supor, também, que os bancos nos enviem periodicamente um segundo arquivo, chamado CRA0212, com as duplicatas pagas num determinado período. A chave desse arquivo também é formada pelos campos NR-FATURA e NR-DUPLICATA:
Seu layout está definido no book CRF0212:
FD CRA0212. 01 CRA0212-REGISTRO. 03 CRA0212-CHAVE. 05 CRA0212-NR-FATURA PIC 9(006). 05 CRA0212-NR-DUPLICATA PIC 9(002). 03 CRA0212-CD-BANCO PIC X(003). 03 CRA0212-DT-PAGAMENTO PIC 9(008). 03 CRA0212-VL-PAGO PIC S9(013)V9(002).
Precisamos construir um programa chamado CRP0213 que vai gerar um terceiro arquivo, chamado CRA0213. Esse arquivo precisa receber todos os registros do arquivo CRA0207V, atualizados com as informações provenientes do arquivo CRA0212:
Repare que apenas os registros cuja chave existe nos dois arquivos é que serão atualizados. Os demais serão gravados no arquivo de saída com os campos CD-BANCO, DT-PAGAMENTO e VL-PAGO vazios (em branco ou zerados, dependendo da picture).
O layout do arquivo de saída está definido no book CRF0213, e contém os campos dos dois arquivos de entrada:
FD CRA0213. 01 CRA0213-REGISTRO. 03 CRA0213-CHAVE. 05 CRA0213-NR-FATURA PIC 9(006). 05 CRA0213-NR-DUPLICATA PIC 9(002). 03 CRA0213-CD-CLIENTE PIC X(006). 03 CRA0213-DT-EMISSAO PIC 9(008). 03 CRA0213-DT-VENCTO PIC 9(008). 03 CRA0213-VL-DUPLICATA PIC S9(013)V9(002). 03 CRA0213-CD-BANCO PIC X(003). 03 CRA0213-DT-PAGAMENTO PIC 9(008). 03 CRA0213-VL-PAGO PIC S9(013)V9(002).
Quando compararmos as chaves dos dois arquivos de entrada existirão três situações possíveis:
- CRA0207V-CHAVE < CRA0212-CHAVE: A duplicata não existe no arquivo de pagamentos que foi enviado pelo banco
- CRA0207V-CHAVE = CRA0212-CHAVE: A duplicata existe no arquivo de pagamentos que foi enviado pelo banco
- CRA0207V-CHAVE > CRA0212-CHAVE: O arquivo enviado pelo banco contém uma duplicata que não existe no nosso arquivo
O segredo do algoritmo para balance-line é definir o que fazer nas três situações possíveis. A figura abaixo mostra, lado a lado, as chaves dos arquivos CRA0207V e CRA0212 à medida em que o programa for lendo os registros desses arquivos.
Repare que, quando as chaves forem iguais, o programa gravará o registro completo no arquivo de saída e lerá o próximo registro tanto do arquivo CRA0207V quanto do arquivo CRA0212.
Quando a chave do arquivo CRA0207V for menor que a chave do arquivo CRA0212 concluiremos que nossa duplicata não existe no arquivo enviado pelo banco. O programa vai gravar o registro incompleto no arquivo de saída e ler o próximo registro do arquivo CRA0207V. O arquivo CRA0212 fica parado.
Quando a chave do arquivo CRA0207V for maior que a chave do arquivo CRA0212 concluiremos que o banco informou o pagamento de uma duplicata que não existe no nosso arquivo. O programa vai ignorar e ler o próximo registro do arquivo CRA0212. O arquivo CRA0207V fica parado.
O programa termina quando não houver mais registros nem no arquivo CRA0207V nem no arquivo CRA0212. Mas é possível que um arquivo termine antes do outro. Para garantir que todos os registros restantes serão processados pelo programa, normalmente movemos o maior valor possível para a chave do arquivo que terminou primeiro. Normalmente, esse maior valor possível é obtido através da constante figurativa HIGH-VALUES, como no exemplo abaixo:
MOVE HIGH-VALUES TO variavel
Mover HIGH-VALUES para uma variável significa preencher todos os seus bytes com o maior hexadecimal possível (x’FF’).
Para entender melhor esse uso do HiGH-VALUES, imagine que no exemplo anterior o arquivo CRA0207V tivesse três registros a mais, e que depois do último registro do arquivo CRA0212 movêssemos HIGH-VALUES para a sua chave. A figura abaixo mostra as decisões que o programa tomaria em cada caso:
Agora imagine o contrário: o arquivo CRA0212 tem três registros a mais e quando o CRA0207V terminar moveremos HIGH-VALUES para a sua chave:
Repare que, tanto no primeiro caso quanto no segundo, o uso do HIGH-VALUES permitiu que todos os registros restantes fossem tratados corretamente pelo programa. Os registros a mais no arquivo CRA0207V seriam gravados no arquivo de saída, como previsto. Os registros a mais no arquivo CRA0212 seriam ignorados, também como havia sido solicitado na especificação. O programa só termina quando os dois arquivos terminam, e as duas chaves estiverem preenchidas com HIGH-VALUES.
Agora que já entendemos o que o programa fará, podemos acompanhar a construção desse balance-line. O diagrama estruturado do programa é o seguinte:
O que faremos em cada parágrafo será o seguinte:
- 1-INICIA: Classificar os dois arquivos de entrada pelos campos NR-FATURA e NR-DUPLICATA. Abrir os dois arquivos de entrada e o arquivo de saída. Ler os primeiros registros dos arquivos de entrada.
- 2-PROCESSA: Se CRA0207V-CHAVE for igual a CRA0212-CHAVE, executar o parágrafo 21-DUPLICATA-PAGA; se for menor, executar o parágrafo 22-DUPLICATA-NAO-PAGA; se for maior executar 23-PAGAMENTO-NAO-ENCONTRADO.
- 21-DUPLICATA-PAGA: Mover os campos NR-FATURA, NR-DUPLICATA, NR-CLIENTE, DT-EMISSAO, DT-VENCIMENTO, VL-DUPLICATA, CD-CATEGORIA e ST-DUPLICATA do arquivo CRA0207V para o arquivo de saída CRA0213. Mover os campos CD-BANCO, DT-PAGAMENTO e VL-PAGO do arquivo CRA0212 para o arquivo CRA0213. Gravar o registro no arquivo CRA0213. Executar o parágrafo X1-LE-CRA0207V. Executar o parágrafo X2-LE-CRA0212.
- 22-DUPLICATA-NAO-PAGA: Mover os campos NR-FATURA, NR-DUPLICATA, NR-CLIENTE, DT-EMISSAO, DT-VENCIMENTO, VL-DUPLICATA, CD-CATEGORIA e ST-DUPLICATA do arquivo CRA0206 para o arquivo de saída CRA0213. Mover espaços para o campo CD-BANCO e zeros para os campos DT-PAGAMENTO e VL-PAGO do arquivo CRA0213. Gravar o registro no arquivo CRA0213. Executar o parágrafo X1-LE-CRA0207V.
- 22-PAGAMENTO-NAO-ENCONTRADO: Executar o parágrafo X2-LE-CRA0212.
- X1-LE-CRA0207V: Lê o próximo registro do arquivo CRA0207V. Se for fim de arquivo (file status diferente de “00”), mover HIGH-VALUES para o campo CRA0207V-CHAVE.
- X1-LE-CRA0212: Lê o próximo registro do arquivo CRA0212. Se for fim de arquivo (file status diferente de “00”), mover HIGH-VALUES para o campo CRA0212-CHAVE.
Agora podemos construir o programa:
*================================================================* IDENTIFICATION DIVISION. *----------------------------------------------------------------* PROGRAM-ID. CRP0213. AUTHOR. PAULO ANDRE DIAS. DATE-WRITTEN. 18/01/2017. REMARKS. *----------------------------------------------------------------* * SISTEMA: CR - CONTAS A RECEBER * JOB: 02 - GERACAO DE FLUXO DE CAIXA * PROGRAMA: 13 - REGISTRA PAGAMENTO DE DUPLICATAS * * OBJETIVO: BALANCE-LINE ENTRE O ARQUIVO DE DUPLICATAS ATI- * VAS E O ARQUIVO DE PAGAMENTOS BANCARIOS PARA * GERACAO DO ARQUIVO DE DUPLICATAS ATUALIZADAS * COM REGISTRO DE PAGAMENTOS * * VERSOES: DATA DESCRICAO * ------ --------------------------------------- * XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *----------------------------------------------------------------*
Na ENVIRONMENT vamos usar os books de declaração para os arquivos de entrada (CRA0207V e CRA0212) e saída (CRA0213). Também declaramos dois arquivos temporários para classificação dos arquivos de entrada:
*================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * DUPLICATAS VALIDAS COPY CRS0207V. * PAGAMENTOS BANCARIOS COPY CRS0212. * DUPLICATAS ATUALIZADAS COM REGISTRO DE PAGAMENTO COPY CRS0213. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO DO ARQUIVO CRA0207V SELECT TMP0207V ASSIGN TO "$DAT/TMP0207V.DAT" ORGANIZATION IS LINE SEQUENTIAL. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO DO ARQUIVO CRA0212 SELECT TMP0212 ASSIGN TO "$DAT/TMP0212.DAT" ORGANIZATION IS LINE SEQUENTIAL.
E faremos a mesma coisa no detalhamento dos arquivos, na DATA DIVISION. Mais uma vez, na descrição dos arquivos temporários, mencionaremos apenas as chaves e completaremos os registros com um FILLER garantindo que eles ficarão com o mesmo tamanho dos registros dos arquivos originais.
*================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * DUPLICATAS VALIDAS COPY CRF0207V. * PAGAMENTOS BANCARIOS COPY CRF0212. * DUPLICATAS ATUALIZADAS COM REGISTRO DE PAGAMENTO COPY CRF0213. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO DO ARQUIVO CRA0207V SD TMP0207V. 01 TMP0207V-REGISTRO. 03 TMP0207V-CHAVE. 05 TMP0207V-NR-FATURA PIC 9(006). 05 TMP0207V-NR-DUPLICATA PIC 9(002). 03 FILLER PIC X(043). * ARQUIVO TEMPORARIO PARA CLASSIFICACAO DO ARQUIVO CRA0212 SD TMP0212. 01 TMP0212-REGISTRO. 03 TMP0212-CHAVE. 05 TMP0212-NR-FATURA PIC 9(006). 05 TMP0212-NR-DUPLICATA PIC 9(002). 03 FILLER PIC X(026).
A WORKING terá apenas as variáveis para file status. Elas não serão usadas nesse programa, mas precisam ser declaradas porque foram mencionadas nos books de declaração da ENVIRONMENT:
*================================================================* WORKING-STORAGE SECTION. *----------------------------------------------------------------* 01 WT-FILE-STATUS. 03 WT-ST-CRA0207V PIC X(002) VALUE SPACES. 03 WT-ST-CRA0212 PIC X(002) VALUE SPACES. 03 WT-ST-CRA0213 PIC X(002) VALUE SPACES.
O parágrafo principal executará o parágrafo 22-PROCESSA até que as chaves dos dois arquivos sejam iguais a HIGH-VALUES:
*================================================================* PROCEDURE DIVISION. *----------------------------------------------------------------* 0-PRINCIPAL. PERFORM 1-INICIA PERFORM 2-PROCESSA UNTIL CRA0207V-CHAVE = HIGH-VALUES AND CRA0212-CHAVE = HIGH-VALUES PERFORM 3-TERMINA STOP RUN.
No parágrafo dos procedimentos iniciais, classificamos os dois arquivos de entrada pelas mesmas chaves. Em seguida, abrimos os arquivos de entrada e saída. Desta vez usamos um formato do comando OPEN que permite abrir todos os arquivos com um comando só. Por último, chamamos os parágrafos que farão as leituras dos primeiros registros:
*----------------------------------------------------------------* * CLASSIFICA OS DOIS ARQUIVOS DE ENTRADA POR NR-FATURA E * NR-DUPLICATA. ABRE OS ARQUIVOS DE ENTRADA E SAIDA. LE O PRI- * MEIRO REGISTRO DOS DOIS ARQUIVOS. *----------------------------------------------------------------* 1-INICIA. SORT TMP0207V ON ASCENDING KEY TMP0207V-NR-FATURA TMP0207V-NR-DUPLICATA USING CRA0207V GIVING CRA0207V SORT TMP0212 ON ASCENDING KEY TMP0212-NR-FATURA TMP0212-NR-DUPLICATA USING CRA0212 GIVING CRA0212 OPEN INPUT CRA0207V CRA0212 OUTPUT CRA0213 PERFORM X1-LE-CRA0207V PERFORM X2-LE-CRA0212.
O balance-line acontece no parágrafo 2-PROCESSA. Observe que primeiro verificamos se as chaves dos dois arquivos são iguais. Sabemos que essas chaves são itens de grupo compostos por NR-FATURA e NR-DUPLICATA, e por isso classificamos os arquivos por esse campos.
Chaves iguais significam que a duplicata existe tanto no arquivo de duplicatas quanto no arquivo de pagamentos e por isso ela será tratada como “duplicata paga”. Se a chave do arquivo de duplicatas (CRA0207V) for menor do que a chave do arquivo de pagamentos (CRA0212) então a duplicata do arquivo CRA0207V não existe no arquivo CRA0212, e por isso ela será tratada como “não paga”. Se a chave do arquivo de duplicatas for maior do que a chave do arquivo de pagamentos então a duplicata do arquivo CRA0212 não existe no arquivo CRA0207V, e por isso será ignorada.
*----------------------------------------------------------------* * SE A DUPLICATA EXISTIR NOS DOIS ARQUIVOS ELA SERA' ATUALIZADA * COM OS DADOS DO PAGAMENTO. *----------------------------------------------------------------* 2-PROCESSA. IF CRA0207V-CHAVE = CRA0212-CHAVE PERFORM 21-DUPLICATA-PAGA ELSE IF CRA0207V-CHAVE < CRA0212-CHAVE PERFORM 22-DUPLICATA-NAO-PAGA ELSE PERFORM 23-PAGAMENTO-IGNORADO END-IF END-IF.
O tratamento de duplicatas pagas implica em gravar no arquivo de saída um registro com todos os campos do arquivo CRA0207V mais as informações sobre o pagamento que vieram no arquivo CRA0212. Em seguida o programa lê o próximo registro dos dois arquivos:
*----------------------------------------------------------------* * GRAVA REGISTRO COMPLETO NO ARQUIVO DE SAIDA, COM DADOS DA DU- * PLICATA E DADOS DO PAGAMENTO *----------------------------------------------------------------* 21-DUPLICATA-PAGA. MOVE CRA0207V-NR-FATURA TO CRA0213-NR-FATURA MOVE CRA0207V-NR-DUPLICATA TO CRA0213-NR-DUPLICATA MOVE CRA0207V-CD-CLIENTE TO CRA0213-CD-CLIENTE MOVE CRA0207V-DT-EMISSAO TO CRA0213-DT-EMISSAO MOVE CRA0207V-DT-VENCIMENTO TO CRA0213-DT-VENCIMENTO MOVE CRA0207V-VL-DUPLICATA TO CRA0213-VL-DUPLICATA MOVE CRA0207V-CD-CATEGORIA TO CRA0213-CD-CATEGORIA MOVE CRA0207V-ST-DUPLICATA TO CRA0213-ST-DUPLICATA MOVE CRA0212-CD-BANCO TO CRA0213-CD-BANCO MOVE CRA0212-DT-PAGAMENTO TO CRA0213-DT-PAGAMENTO MOVE CRA0212-VL-PAGO TO CRA0213-VL-PAGO WRITE CRA0213-REGISTRO PERFORM X1-LE-CRA0207V PERFORM X2-LE-CRA0212.
O tratamento de duplicata não paga implica em gravar no arquivo de saída todos os campos do arquivo CRA0207V, sem informações sobre pagamento. Movemos espaços para o campo CD-BANCO porque ele é alfanumérico; movemos zeros para os dois outros campos porque eles são numéricos.
Dessa vez, lemos o próximo registro apenas do arquivo CRA0207V. Fazemos isso porque, nesse momento, a chave de CRA0207V é menor do que a chave de CRA0212. E o arquivo CRA0212 ficará parado até que as chaves sejam iguais novamente.
*----------------------------------------------------------------*
* GRAVA REGISTRO INCOMPLETO NO ARQUIVO DE SAIDA PORQUE A DUPLI-
* CATA NAO FOI ENCONTRADA NO ARQUIVO DE PAGAMENTOS BANCARIOS
*----------------------------------------------------------------*
22-DUPLICATA-NAO-PAGA.
MOVE CRA0207V-NR-FATURA TO CRA0213-NR-FATURA
MOVE CRA0207V-NR-DUPLICATA TO CRA0213-NR-DUPLICATA
MOVE CRA0207V-CD-CLIENTE TO CRA0213-CD-CLIENTE
MOVE CRA0207V-DT-EMISSAO TO CRA0213-DT-EMISSAO
MOVE CRA0207V-DT-VENCIMENTO TO CRA0213-DT-VENCIMENTO
MOVE CRA0207V-VL-DUPLICATA TO CRA0213-VL-DUPLICATA
MOVE CRA0207V-CD-CATEGORIA TO CRA0213-CD-CATEGORIA
MOVE CRA0207V-ST-DUPLICATA TO CRA0213-ST-DUPLICATA
MOVE SPACES TO CRA0213-CD-BANCO
MOVE ZEROS TO CRA0213-DT-PAGAMENTO
MOVE ZEROS TO CRA0213-VL-PAGO
WRITE CRA0213-REGISTRO
PERFORM X1-LE-CRA0207V.
Para ignorar o pagamento que não encontramos no arquivo de duplicatas, simplesmente lemos o próximo registro do arquivo de pagamentos. Lembre-se que, nesse momento, a chave do arquivo CRA0212 está menor do que a chave do arquivo CRA0207V, e, desta vez, é o arquivo CRA0207V que vai ficar parado esperando que as chaves fiquem iguais novamente.
*----------------------------------------------------------------*
* EXISTE UMA DUPLICATA NO ARQUIVO GERADO PELO BANCO QUE NAO FOI
* ENCONTRADA NO ARQUIVO DE DUPLICATAS ATIVAS. O REGISTRO SERA'
* IGNORADO.
*----------------------------------------------------------------*
23-PAGAMENTO-IGNORADO.
PERFORM X2-LE-CRA0212.
Os parágrafos que fazem a leitura de registros nos arquivos de entrada usam o comando READ com a opção AT END para mover HIGH-VALUES para as chaves. Essa movimentação acontecerá apenas quando não existirem mais registros a serem lidos.
*----------------------------------------------------------------* * LE PROXIMO REGISTRO DO ARQUIVO DE DUPLICATAS VALIDAS. SE FOR * FIM DE ARQUIVO MOVE HIGH-VALUES PARA A CHAVE *----------------------------------------------------------------* X1-LE-CRA0207V. READ CRA0207V AT END MOVE HIGH-VALUES TO CRA0207V-CHAVE. *----------------------------------------------------------------* * LE PROXIMO REGISTRO DO ARQUIVO DE PAGAMENTOS. SE FOR * FIM DE ARQUIVO MOVE HIGH-VALUES PARA A CHAVE *----------------------------------------------------------------* X2-LE-CRA0212. READ CRA0212 AT END MOVE HIGH-VALUES TO CRA0212-CHAVE.
Também poderíamos testar o file status de cada arquivo, ao invés de usar a cláusula AT END, como no exemplo abaixo.
READ CRA0207V IF WT-ST-CRA0207V NOT = “00” MOVE HIGH-VALUES TO CRA0207V-CHAVE END-IF.
O efeito seria o mesmo e talvez fosse a melhor opção se houvesse mais comandos a executar na situação de fim de arquivo, o que não foi o caso dessa vez.
Esse é o programa completo:
*================================================================* IDENTIFICATION DIVISION. *----------------------------------------------------------------* PROGRAM-ID. CRP0213. AUTHOR. PAULO ANDRE DIAS. DATE-WRITTEN. 18/01/2017. REMARKS. *----------------------------------------------------------------* * SISTEMA: CR - CONTAS A RECEBER * JOB: 02 - GERACAO DE FLUXO DE CAIXA * PROGRAMA: 13 - REGISTRA PAGAMENTO DE DUPLICATAS * * OBJETIVO: BALANCE-LINE ENTRE O ARQUIVO DE DUPLICATAS ATI- * VAS E O ARQUIVO DE PAGAMENTOS BANCARIOS PARA * GERACAO DO ARQUIVO DE DUPLICATAS ATUALIZADAS * COM REGISTRO DE PAGAMENTOS * * VERSOES: DATA DESCRICAO * ------ --------------------------------------- * XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *----------------------------------------------------------------* *================================================================* ENVIRONMENT DIVISION. *----------------------------------------------------------------* CONFIGURATION SECTION. SPECIAL-NAMES. DECIMAL-POINT IS COMMA. INPUT-OUTPUT SECTION. FILE-CONTROL. * DUPLICATAS VALIDAS COPY CRS0207V. * PAGAMENTOS BANCARIOS COPY CRS0212. * DUPLICATAS ATUALIZADAS COM REGISTRO DE PAGAMENTO COPY CRS0213. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO DO ARQUIVO CRA0207V SELECT TMP0207V ASSIGN TO "$DAT/TMP0207V.DAT" ORGANIZATION IS LINE SEQUENTIAL. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO DO ARQUIVO CRA0212 SELECT TMP0212 ASSIGN TO "$DAT/TMP0212.DAT" ORGANIZATION IS LINE SEQUENTIAL. *================================================================* DATA DIVISION. *----------------------------------------------------------------* FILE SECTION. * DUPLICATAS VALIDAS COPY CRF0207V. * PAGAMENTOS BANCARIOS COPY CRF0212. * DUPLICATAS ATUALIZADAS COM REGISTRO DE PAGAMENTO COPY CRF0213. * ARQUIVO TEMPORARIO PARA CLASSIFICACAO DO ARQUIVO CRA0207V SD TMP0207V. 01 TMP0207V-REGISTRO. 03 TMP0207V-CHAVE. 05 TMP0207V-NR-FATURA PIC 9(006). 05 TMP0207V-NR-DUPLICATA PIC 9(002). 03 FILLER PIC X(043). * ARQUIVO TEMPORARIO PARA CLASSIFICACAO DO ARQUIVO CRA0212 SD TMP0212. 01 TMP0212-REGISTRO. 03 TMP0212-CHAVE. 05 TMP0212-NR-FATURA PIC 9(006). 05 TMP0212-NR-DUPLICATA PIC 9(002). 03 FILLER PIC X(026). *================================================================* WORKING-STORAGE SECTION. *----------------------------------------------------------------* 01 WT-FILE-STATUS. 03 WT-ST-CRA0207V PIC X(002) VALUE SPACES. 03 WT-ST-CRA0212 PIC X(002) VALUE SPACES. 03 WT-ST-CRA0213 PIC X(002) VALUE SPACES. *================================================================* PROCEDURE DIVISION. *----------------------------------------------------------------* 0-PRINCIPAL. PERFORM 1-INICIA PERFORM 2-PROCESSA UNTIL CRA0207V-CHAVE = HIGH-VALUES AND CRA0212-CHAVE = HIGH-VALUES PERFORM 3-TERMINA STOP RUN. *----------------------------------------------------------------* * CLASSIFICA OS DOIS ARQUIVOS DE ENTRADA POR NR-FATURA E * NR-DUPLICATA. ABRE OS ARQUIVOS DE ENTRADA E SAIDA. LE O PRI- * MEIRO REGISTRO DOS DOIS ARQUIVOS. *----------------------------------------------------------------* 1-INICIA. SORT TMP0207V ON ASCENDING KEY TMP0207V-NR-FATURA TMP0207V-NR-DUPLICATA USING CRA0207V GIVING CRA0207V SORT TMP0212 ON ASCENDING KEY TMP0212-NR-FATURA TMP0212-NR-DUPLICATA USING CRA0212 GIVING CRA0212 OPEN INPUT CRA0207V OPEN INPUT CRA0212 OPEN OUTPUT CRA0213 PERFORM X1-LE-CRA0207V PERFORM X2-LE-CRA0212. *----------------------------------------------------------------* * SE A DUPLICATA EXISTIR NOS DOIS ARQUIVOS ELA SERA' ATUALIZADA * COM OS DADOS DO PAGAMENTO. *----------------------------------------------------------------* 2-PROCESSA. IF CRA0207V-CHAVE = CRA0212-CHAVE PERFORM 21-DUPLICATA-PAGA ELSE IF CRA0207V-CHAVE < CRA0212-CHAVE PERFORM 22-DUPLICATA-NAO-PAGA ELSE PERFORM 23-PAGAMENTO-IGNORADO END-IF END-IF. *----------------------------------------------------------------* * FECHA ARQUIVOS *----------------------------------------------------------------* 3-TERMINA. CLOSE CRA0207V CRA0212 CRA0213. *----------------------------------------------------------------* * GRAVA REGISTRO COMPLETO NO ARQUIVO DE SAIDA, COM DADOS DA DU- * PLICATA E DADOS DO PAGAMENTO *----------------------------------------------------------------* 21-DUPLICATA-PAGA. MOVE CRA0207V-NR-FATURA TO CRA0213-NR-FATURA MOVE CRA0207V-NR-DUPLICATA TO CRA0213-NR-DUPLICATA MOVE CRA0207V-CD-CLIENTE TO CRA0213-CD-CLIENTE MOVE CRA0207V-DT-EMISSAO TO CRA0213-DT-EMISSAO MOVE CRA0207V-DT-VENCIMENTO TO CRA0213-DT-VENCIMENTO MOVE CRA0207V-VL-DUPLICATA TO CRA0213-VL-DUPLICATA MOVE CRA0207V-CD-CATEGORIA TO CRA0213-CD-CATEGORIA MOVE CRA0207V-ST-DUPLICATA TO CRA0213-ST-DUPLICATA MOVE CRA0212-CD-BANCO TO CRA0213-CD-BANCO MOVE CRA0212-DT-PAGAMENTO TO CRA0213-DT-PAGAMENTO MOVE CRA0212-VL-PAGO TO CRA0213-VL-PAGO WRITE CRA0213-REGISTRO PERFORM X1-LE-CRA0207V PERFORM X2-LE-CRA0212. *----------------------------------------------------------------* * GRAVA REGISTRO INCOMPLETO NO ARQUIVO DE SAIDA PORQUE A DUPLI- * CATA NAO FOI ENCONTRADA NO ARQUIVO DE PAGAMENTOS BANCARIOS *----------------------------------------------------------------* 22-DUPLICATA-NAO-PAGA. MOVE CRA0207V-NR-FATURA TO CRA0213-NR-FATURA MOVE CRA0207V-NR-DUPLICATA TO CRA0213-NR-DUPLICATA MOVE CRA0207V-CD-CLIENTE TO CRA0213-CD-CLIENTE MOVE CRA0207V-DT-EMISSAO TO CRA0213-DT-EMISSAO MOVE CRA0207V-DT-VENCIMENTO TO CRA0213-DT-VENCIMENTO MOVE CRA0207V-VL-DUPLICATA TO CRA0213-VL-DUPLICATA MOVE CRA0207V-CD-CATEGORIA TO CRA0213-CD-CATEGORIA MOVE CRA0207V-ST-DUPLICATA TO CRA0213-ST-DUPLICATA MOVE SPACES TO CRA0213-CD-BANCO MOVE ZEROS TO CRA0213-DT-PAGAMENTO MOVE ZEROS TO CRA0213-VL-PAGO WRITE CRA0213-REGISTRO PERFORM X1-LE-CRA0207V. *----------------------------------------------------------------* * EXISTE UMA DUPLICATA NO ARQUIVO GERADO PELO BANCO QUE NAO FOI * ENCONTRADA NO ARQUIVO DE DUPLICATAS ATIVAS. O REGISTRO SERA' * IGNORADO. *----------------------------------------------------------------* 23-PAGAMENTO-IGNORADO. PERFORM X2-LE-CRA0212. *----------------------------------------------------------------* * LE PROXIMO REGISTRO DO ARQUIVO DE DUPLICATAS VALIDAS. SE FOR * FIM DE ARQUIVO MOVE HIGH-VALUES PARA A CHAVE *----------------------------------------------------------------* X1-LE-CRA0207V. READ CRA0207V AT END MOVE HIGH-VALUES TO CRA0207V-CHAVE. *----------------------------------------------------------------* * LE PROXIMO REGISTRO DO ARQUIVO DE PAGAMENTOS. SE FOR * FIM DE ARQUIVO MOVE HIGH-VALUES PARA A CHAVE *----------------------------------------------------------------* X2-LE-CRA0212. READ CRA0212 AT END MOVE HIGH-VALUES TO CRA0212-CHAVE.
Anterior | Conteúdo | Próxima |