MODULE 7 · WORKING WITH TEXT · 8/8

Challenge: customer feed loader

30 min60 XPExercise

A new online-banking partner sends new and updated customers as a CSV feed. Your job is the front half of the loader program: validate every line, clean up what passes, and turn it into the bank's fixed-width customer record. Anything that fails is rejected with a reason, so the operations team can go back to the partner.

This is exactly the kind of program interface teams write and maintain, and it uses everything in this module:

  • UNSTRING with TALLYING and ON OVERFLOW to split each line and check the field count,
  • intrinsic functions (TRIM, UPPER-CASE, LENGTH) to cleanse,
  • reference modification and a class test to check the key format,
  • TEST-NUMVAL-C and NUMVAL-C to validate and convert the balance.

Validate in order, report the first failure

When a record can fail several rules, a common pattern is an EVALUATE TRUE with one WHEN per rule, in priority order. The first rule that fails sets the reason, and the rest are skipped:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. RULES.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-CODE      PIC X(6) VALUE "C0A042".
       01  WS-REASON    PIC X(20).
       PROCEDURE DIVISION.
           MOVE SPACES TO WS-REASON
           EVALUATE TRUE
               WHEN WS-CODE(1:1) NOT = "C"
                   MOVE "WRONG PREFIX" TO WS-REASON
               WHEN WS-CODE(2:5) IS NOT NUMERIC
                   MOVE "NOT NUMERIC" TO WS-REASON
           END-EVALUATE
           IF WS-REASON = SPACES
               DISPLAY "VALID"
           ELSE
               DISPLAY "INVALID: " WS-REASON
           END-IF
           STOP RUN.

Output: INVALID: NOT NUMERIC. IS NUMERIC on an alphanumeric item (or a reference-modified piece of one) is true only if every character is a digit.

Fixed-width output

Build the output record as a group of elementary fields and MOVE into each one. The group is then the record, with every field at a fixed position, and moving a trimmed value into a field pads or truncates it for you. A numeric-edited field such as PIC -9(7).99 gives a fixed-width amount with a sign position that is a space for positive values.

On the job

Rejects are never silently dropped in production. A loader writes them to an exceptions file with the reason, and the job's control totals (records read, accepted, rejected) must add up, because auditors check them. In the next module you will write real files instead of displaying lines.

Your task

Read CSV lines until one says END. Each line should be:

CUSTOMER-ID,NAME,CITY,BALANCE

Number the lines from 1. Check each line against these rules in this order and stop at the first failure:

Rule Reason
Exactly 4 fields (no more, no fewer) FIELD COUNT
ID, once trimmed and upper-cased, is 6 characters: C followed by 5 digits (c00107 is fine) BAD ID
Name is not blank MISSING NAME
Balance is a valid amount according to TEST-NUMVAL-C (may have $, spaces, a sign or CR) BAD BALANCE

For a rejected line, display REJECT LINE nn: reason, e.g. REJECT LINE 03: BAD ID.

For a valid line, fill OUT-REC and display it. It is a fixed-width customer record (already declared in the starter):

Field Picture Content
OUT-ID X(6) the upper-cased ID
OUT-NAME X(20) the name, trimmed and upper-cased
OUT-CITY X(15) the city, trimmed and upper-cased
OUT-BAL -9(7).99 the balance, converted with NUMVAL-C

At the end, display the control totals:

READ: 04 ACCEPTED: 03 REJECTED: 01

Example: this input

C00042, Grace Hopper ,Arlington,$1234.56
c00107,alan turing,wilmslow,-20.00
X00001,Ada Lovelace,London,10.00
END

produces

C00042GRACE HOPPER        ARLINGTON       0001234.56
C00107ALAN TURING         WILMSLOW       -0000020.00
REJECT LINE 03: BAD ID
READ: 03 ACCEPTED: 02 REJECTED: 01
fixed format
Run your program to see its output here. The first visible test's input and datasets are used.
Submit to grade your program against every test.