*> ============================================================ *> 15-csv-fb-nolf : 外部CDR CSV取込(无LF) (CDR Import) *> Input : FILE-IN (file-in.csv) Output: FILE-OUT (file-out.dat) *> ERROR-REPORT (csv-errors.dat) AUDIT-FILE (csv-audit.dat) *> Enhanced: SECTIONs, field validation, truncation detection, *> quote check, count reconciliation, hash totals, *> severity levels, FILE STATUS after every I/O. *> Coverage: CF-N001, CF-N003~N006, CF-A002 *> ============================================================ IDENTIFICATION DIVISION. PROGRAM-ID. CsvFbNoLf. ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT FILE-IN ASSIGN TO 'file-in.csv' ORGANIZATION IS LINE SEQUENTIAL FILE STATUS IS WS-FILE-IN-STATUS. SELECT FILE-OUT ASSIGN TO 'file-out.dat' ORGANIZATION IS SEQUENTIAL FILE STATUS IS WS-FILE-OUT-STATUS. SELECT ERROR-REPORT ASSIGN TO 'csv-errors.dat' ORGANIZATION IS LINE SEQUENTIAL FILE STATUS IS WS-ERR-STATUS. SELECT AUDIT-FILE ASSIGN TO 'csv-audit.dat' ORGANIZATION IS LINE SEQUENTIAL FILE STATUS IS WS-AUDIT-STATUS. DATA DIVISION. FILE SECTION. FD FILE-IN. 01 IN-LINE PIC X(200). FD FILE-OUT. 01 OUT-REC PIC X(100). FD ERROR-REPORT. 01 ERR-REC PIC X(80). FD AUDIT-FILE. 01 AUD-REC PIC X(80). WORKING-STORAGE SECTION. 01 WS-TELECOM-REC. COPY "telecom/TEL-CDR.cpy". *> File status keys for all files 01 WS-FILE-IN-STATUS PIC X(02). 01 WS-FILE-OUT-STATUS PIC X(02). 01 WS-ERR-STATUS PIC X(02). 01 WS-AUDIT-STATUS PIC X(02). *> EOF flag 01 WS-EOF PIC X(01) VALUE 'N'. 88 WS-EOF-YES VALUE 'Y' FALSE 'N'. *> Record counters (original + expanded) 01 WS-REC-COUNT PIC 9(05) VALUE ZERO. 01 WS-LINE-COUNT PIC 9(05) VALUE ZERO. 01 WS-COUNTERS. 05 WS-TOT-READ PIC 9(09) VALUE ZERO. 05 WS-TOT-WRITTEN PIC 9(09) VALUE ZERO. 05 WS-TOT-MALFORMED PIC 9(09) VALUE ZERO. 05 WS-TOT-TRUNCATED PIC 9(09) VALUE ZERO. 05 WS-TOT-QUOTE-ERR PIC 9(09) VALUE ZERO. 05 WS-TOT-FIELD-ERR PIC 9(09) VALUE ZERO. 05 WS-TOT-WARNINGS PIC 9(09) VALUE ZERO. 05 WS-TOT-ERRORS PIC 9(09) VALUE ZERO. 05 WS-TOT-CRITICAL PIC 9(09) VALUE ZERO. *> CSV parsing fields (original) 01 WS-FIELDS. 05 WS-FIELD1 PIC X(30). 05 WS-FIELD2 PIC X(30). 05 WS-FIELD3 PIC X(30). 01 WS-POS PIC 9(03). 01 WS-CHAR PIC X(01). 01 WS-FIELD-NUM PIC 9(01). 01 WS-CHAR-OFFSET PIC 9(02). 01 WS-IN-QUOTE PIC X(01) VALUE 'N'. 88 WS-IN-QUOTE-YES VALUE 'Y' FALSE 'N'. 01 WS-OUT-LINE PIC X(100). *> Validation fields 01 WS-QUOTE-COUNT PIC 9(03) VALUE ZERO. 01 WS-MALFORMED-CNT PIC 9(05) VALUE ZERO. 01 WS-TRUNC-CNT PIC 9(05) VALUE ZERO. 01 WS-FIELD-COUNT-ERR PIC 9(05) VALUE ZERO. 01 WS-FIELD3-LEN PIC 9(02). 01 WS-IDX PIC 9(02). 01 WS-CHAR-CHK PIC X(01). 01 WS-ALL-NUMERIC PIC X(01) VALUE 'Y'. 01 WS-RECONCILE-OK PIC X(01) VALUE 'Y'. 01 WS-VALID-FLAG PIC X(01) VALUE 'Y'. 88 WS-VALID-REC-YES VALUE 'Y' FALSE 'N'. 88 WS-VALID-REC-NO VALUE 'N'. *> Field-level validation status per field 01 WS-FIELD-STATUS. 05 WS-FIELD1-OK PIC X(01) VALUE 'Y'. 05 WS-FIELD2-OK PIC X(01) VALUE 'Y'. 05 WS-FIELD3-OK PIC X(01) VALUE 'Y'. 01 WS-FIELD-ERR-MSG PIC X(60). 01 WS-FIELD-TRUNC-FLAG PIC X(01) VALUE 'N'. 88 WS-FIELD-TRUNC-YES VALUE 'Y' FALSE 'N'. *> Input record buffer for error reporting 01 WS-INPUT-BUF PIC X(200). *> Hash totals (per field + overall) 01 WS-HASH-TOTAL PIC 9(10) VALUE ZERO. 01 WS-HASH-FIELD-LEN PIC 9(05) VALUE ZERO. 01 WS-HASH-FIELD1 PIC 9(10) VALUE ZERO. 01 WS-HASH-FIELD2 PIC 9(10) VALUE ZERO. 01 WS-HASH-FIELD3 PIC 9(10) VALUE ZERO. *> Timestamp 01 WS-TIMESTAMP PIC X(20). 01 WS-CUR-DATE-DISP PIC X(08). 01 WS-CUR-TIME-DISP PIC X(06). 01 WS-TS-DISPLAY PIC X(26). *> Error severity levels 01 WS-SEVERITY PIC X(01). 88 WS-SEV-INFO VALUE 'I'. 88 WS-SEV-WARNING VALUE 'W'. 88 WS-SEV-ERROR VALUE 'E'. 88 WS-SEV-CRITICAL VALUE 'C'. *> Report and display formatting fields 01 WS-RPT-COUNT PIC Z(9)9. 01 WS-RPT-COUNT9 PIC Z(9)9. 01 WS-RPT-LINE PIC X(80). 01 WS-RPT-PCT PIC Z(9)9.9. 01 WS-LINE-DISP PIC Z(9)9. 01 WS-DISP-BUFFER PIC X(80). PROCEDURE DIVISION. *> ===== MAIN SECTION ===== MAIN SECTION. MB-PROCESS. PERFORM 1000-INIT PERFORM 2000-OPEN-FILES PERFORM 3000-READ-INPUT PERFORM 4000-REPORT PERFORM 5000-AUDIT PERFORM 6000-ERROR-HANDLE PERFORM 9000-EXIT . *> ===== 1000-INIT ===== 1000-INIT SECTION. 1000-START. MOVE FUNCTION CURRENT-DATE (1:8) TO WS-CUR-DATE-DISP. MOVE FUNCTION CURRENT-DATE (9:6) TO WS-CUR-TIME-DISP. STRING WS-CUR-DATE-DISP '-' WS-CUR-TIME-DISP DELIMITED BY SIZE INTO WS-TIMESTAMP. STRING WS-CUR-DATE-DISP(1:4) '-' WS-CUR-DATE-DISP(5:2) '-' WS-CUR-DATE-DISP(7:2) ' ' WS-CUR-TIME-DISP(1:2) ':' WS-CUR-TIME-DISP(3:2) ':' WS-CUR-TIME-DISP(5:2) DELIMITED BY SIZE INTO WS-TS-DISPLAY. DISPLAY WS-TS-DISPLAY ' 1000-INIT: CsvFbNoLf START'. *> Initialize all counters to zero MOVE ZERO TO WS-REC-COUNT WS-LINE-COUNT WS-TOT-READ WS-TOT-WRITTEN WS-TOT-MALFORMED WS-TOT-TRUNCATED WS-TOT-QUOTE-ERR WS-TOT-FIELD-ERR WS-TOT-WARNINGS WS-TOT-ERRORS WS-TOT-CRITICAL WS-MALFORMED-CNT WS-TRUNC-CNT WS-FIELD-COUNT-ERR WS-HASH-TOTAL WS-HASH-FIELD1 WS-HASH-FIELD2 WS-HASH-FIELD3 WS-HASH-FIELD-LEN. *> Initialize flags MOVE 'Y' TO WS-RECONCILE-OK. MOVE 'N' TO WS-EOF. *> ===== 2000-OPEN-FILES ===== 2000-OPEN-FILES SECTION. 2000-START. DISPLAY WS-TS-DISPLAY ' 2000-OPEN-FILES: Opening files'. OPEN INPUT FILE-IN. IF WS-FILE-IN-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' ERROR: Cannot open FILE-IN,' ' status: ' WS-FILE-IN-STATUS MOVE 1 TO RETURN-CODE STOP RUN END-IF. DISPLAY WS-TS-DISPLAY ' FILE-IN opened, status=' WS-FILE-IN-STATUS. OPEN OUTPUT FILE-OUT. IF WS-FILE-OUT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' ERROR: Cannot open FILE-OUT,' ' status: ' WS-FILE-OUT-STATUS MOVE 1 TO RETURN-CODE STOP RUN END-IF. DISPLAY WS-TS-DISPLAY ' FILE-OUT opened, status=' WS-FILE-OUT-STATUS. OPEN OUTPUT ERROR-REPORT. IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' ERROR: Cannot open ERROR-REPORT,' ' status: ' WS-ERR-STATUS MOVE 1 TO RETURN-CODE STOP RUN END-IF. DISPLAY WS-TS-DISPLAY ' ERROR-REPORT opened, status=' WS-ERR-STATUS. OPEN OUTPUT AUDIT-FILE. IF WS-AUDIT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' ERROR: Cannot open AUDIT-FILE,' ' status: ' WS-AUDIT-STATUS MOVE 1 TO RETURN-CODE STOP RUN END-IF. DISPLAY WS-TS-DISPLAY ' AUDIT-FILE opened, status=' WS-AUDIT-STATUS. *> Write error report header lines MOVE 'CSV Error Detail Report - CsvFbNoLf' TO ERR-REC. WRITE ERR-REC. IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY 'WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF. STRING 'Run: ' WS-TIMESTAMP INTO ERR-REC. WRITE ERR-REC. IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY 'WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF. MOVE SPACES TO ERR-REC. MOVE 'Line Severity Field Description' TO ERR-REC. WRITE ERR-REC. IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY 'WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF. MOVE SPACES TO ERR-REC. WRITE ERR-REC. IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY 'WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF. *> Write audit file header lines MOVE 'CSV Audit Report - CsvFbNoLf' TO AUD-REC. WRITE AUD-REC. IF WS-AUDIT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: AUDIT-FILE status=' WS-AUDIT-STATUS END-IF. STRING 'Run: ' WS-TIMESTAMP INTO AUD-REC. WRITE AUD-REC. IF WS-AUDIT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: AUDIT-FILE status=' WS-AUDIT-STATUS END-IF. MOVE SPACES TO AUD-REC. WRITE AUD-REC. IF WS-AUDIT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: AUDIT-FILE status=' WS-AUDIT-STATUS END-IF. DISPLAY WS-TS-DISPLAY '2000-OPEN-FILES: All files opened OK'. *> ===== 3000-READ-INPUT ===== 3000-READ-INPUT SECTION. 3000-START. DISPLAY WS-TS-DISPLAY ' 3000-READ-INPUT: Processing CSV'. PERFORM UNTIL WS-EOF-YES READ FILE-IN AT END SET WS-EOF-YES TO TRUE DISPLAY WS-TS-DISPLAY ' EOF reached,' ' lines=' WS-LINE-COUNT NOT AT END ADD 1 TO WS-LINE-COUNT ADD 1 TO WS-TOT-READ MOVE IN-LINE TO WS-INPUT-BUF PERFORM 3100-VALIDATE-RECORD END-READ IF WS-FILE-IN-STATUS NOT = '00' AND NOT = '10' DISPLAY WS-TS-DISPLAY ' READ ERROR: FILE-IN status=' WS-FILE-IN-STATUS MOVE 'E' TO WS-SEVERITY ADD 1 TO WS-TOT-ERRORS END-IF END-PERFORM. *> Close input file with STATUS check CLOSE FILE-IN. IF WS-FILE-IN-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' CLOSE ERROR: FILE-IN status=' WS-FILE-IN-STATUS ADD 1 TO WS-TOT-ERRORS END-IF. DISPLAY WS-TS-DISPLAY ' 3000-READ-INPUT: Input closed,' ' total=' WS-LINE-COUNT. *> ===== 3100-VALIDATE-RECORD ===== 3100-VALIDATE-RECORD SECTION. 3100-START. MOVE 'Y' TO WS-VALID-FLAG. MOVE 'Y' TO WS-FIELD1-OK. MOVE 'Y' TO WS-FIELD2-OK. MOVE 'Y' TO WS-FIELD3-OK. MOVE 'N' TO WS-FIELD-TRUNC-FLAG. MOVE SPACES TO WS-FIELD-ERR-MSG. *> Quote pairing: odd quote count = malformed MOVE 0 TO WS-QUOTE-COUNT. INSPECT IN-LINE TALLYING WS-QUOTE-COUNT FOR ALL '"'. IF FUNCTION MOD(WS-QUOTE-COUNT, 2) NOT = 0 ADD 1 TO WS-MALFORMED-CNT ADD 1 TO WS-TOT-QUOTE-ERR ADD 1 TO WS-TOT-ERRORS MOVE 'E' TO WS-SEVERITY MOVE 'Unpaired quote in CSV line' TO WS-FIELD-ERR-MSG MOVE 'N' TO WS-VALID-FLAG STRING WS-LINE-COUNT ' ERROR Quote ' WS-FIELD-ERR-MSG DELIMITED BY SIZE INTO ERR-REC WRITE ERR-REC IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF DISPLAY WS-TS-DISPLAY ' Quote error at line ' WS-LINE-COUNT ': ' WS-FIELD-ERR-MSG END-IF. *> Parse using original logic -> WS-FIELD1..3 PERFORM PARSE-CSV-LINE. *> Field count: expect 3 fields IF WS-FIELD-NUM < 3 ADD 1 TO WS-FIELD-COUNT-ERR ADD 1 TO WS-TOT-FIELD-ERR ADD 1 TO WS-TOT-ERRORS MOVE 'E' TO WS-SEVERITY MOVE 'N' TO WS-VALID-FLAG STRING 'Expected 3 fields, got ' WS-FIELD-NUM INTO WS-FIELD-ERR-MSG STRING WS-LINE-COUNT ' ERROR Field ' WS-FIELD-ERR-MSG DELIMITED BY SIZE INTO ERR-REC WRITE ERR-REC IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF DISPLAY WS-TS-DISPLAY ' Field count error at line ' WS-LINE-COUNT ': ' WS-FIELD-ERR-MSG END-IF. *> Field 1: presence check IF WS-FIELD1 = SPACES MOVE 'N' TO WS-FIELD1-OK MOVE 'W' TO WS-SEVERITY ADD 1 TO WS-TOT-WARNINGS MOVE 'Field1 is empty' TO WS-FIELD-ERR-MSG STRING WS-LINE-COUNT ' WARNING Field1 ' 'Empty field content' DELIMITED BY SIZE INTO ERR-REC WRITE ERR-REC IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF END-IF. *> Truncation check: Field1 > 30 chars IF FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD1)) > 30 ADD 1 TO WS-TRUNC-CNT ADD 1 TO WS-TOT-TRUNCATED MOVE 'Y' TO WS-FIELD-TRUNC-FLAG MOVE 'W' TO WS-SEVERITY ADD 1 TO WS-TOT-WARNINGS STRING WS-LINE-COUNT ' WARNING Field1 ' 'Truncated to 30 chars' DELIMITED BY SIZE INTO ERR-REC WRITE ERR-REC IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF END-IF. *> Truncation check: Field2 > 30 chars IF FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD2)) > 30 ADD 1 TO WS-TRUNC-CNT ADD 1 TO WS-TOT-TRUNCATED MOVE 'Y' TO WS-FIELD-TRUNC-FLAG MOVE 'W' TO WS-SEVERITY ADD 1 TO WS-TOT-WARNINGS STRING WS-LINE-COUNT ' WARNING Field2 ' 'Truncated to 30 chars' DELIMITED BY SIZE INTO ERR-REC WRITE ERR-REC IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF END-IF. *> Numeric check: Field2 IF WS-FIELD2 NOT = SPACES MOVE 'Y' TO WS-ALL-NUMERIC MOVE WS-FIELD2 TO WS-DISP-BUFFER INSPECT WS-DISP-BUFFER REPLACING ALL SPACES BY '0' PERFORM VARYING WS-IDX FROM 1 BY 1 UNTIL WS-IDX > 30 OR WS-ALL-NUMERIC = 'N' MOVE WS-DISP-BUFFER(WS-IDX:1) TO WS-CHAR-CHK IF WS-CHAR-CHK < '0' OR > '9' MOVE 'N' TO WS-ALL-NUMERIC END-IF END-PERFORM IF WS-ALL-NUMERIC = 'N' MOVE 'N' TO WS-FIELD2-OK MOVE 'W' TO WS-SEVERITY ADD 1 TO WS-TOT-WARNINGS STRING WS-LINE-COUNT ' WARNING Field2 ' 'Non-numeric content' DELIMITED BY SIZE INTO ERR-REC WRITE ERR-REC IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF END-IF END-IF. *> Truncation check: Field3 > 30 chars IF FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD3)) > 30 ADD 1 TO WS-TRUNC-CNT ADD 1 TO WS-TOT-TRUNCATED MOVE 'Y' TO WS-FIELD-TRUNC-FLAG MOVE 'W' TO WS-SEVERITY ADD 1 TO WS-TOT-WARNINGS STRING WS-LINE-COUNT ' WARNING Field3 ' 'Truncated to 30 chars' DELIMITED BY SIZE INTO ERR-REC WRITE ERR-REC IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF END-IF. *> Numeric check: Field3 IF WS-FIELD3 > SPACES MOVE 'Y' TO WS-ALL-NUMERIC MOVE WS-FIELD3 TO WS-DISP-BUFFER INSPECT WS-DISP-BUFFER REPLACING ALL SPACES BY '0' PERFORM VARYING WS-IDX FROM 1 BY 1 UNTIL WS-IDX > 30 OR WS-ALL-NUMERIC = 'N' MOVE WS-DISP-BUFFER(WS-IDX:1) TO WS-CHAR-CHK IF WS-CHAR-CHK < '0' OR > '9' MOVE 'N' TO WS-ALL-NUMERIC END-IF END-PERFORM IF WS-ALL-NUMERIC = 'N' MOVE 'N' TO WS-FIELD3-OK MOVE 'W' TO WS-SEVERITY ADD 1 TO WS-TOT-WARNINGS STRING WS-LINE-COUNT ' WARNING Field3 ' 'Non-numeric content' DELIMITED BY SIZE INTO ERR-REC WRITE ERR-REC IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF END-IF END-IF. *> If record is valid (no ERROR severity), process it IF WS-VALID-FLAG = 'Y' PERFORM 3200-PROCESS-RECORD ELSE DISPLAY WS-TS-DISPLAY ' Record ' WS-LINE-COUNT ' skipped due to validation errors' END-IF. *> ===== 3200-PROCESS-RECORD ===== 3200-PROCESS-RECORD SECTION. 3200-START. PERFORM 3300-WRITE-OUTPUT. *> Update per-field hash totals for batch control COMPUTE WS-HASH-FIELD1 = WS-HASH-FIELD1 + FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD1)) COMPUTE WS-HASH-FIELD2 = WS-HASH-FIELD2 + FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD2)) COMPUTE WS-HASH-FIELD3 = WS-HASH-FIELD3 + FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD3)). *> ===== 3300-WRITE-OUTPUT ===== 3300-WRITE-OUTPUT SECTION. 3300-START. *> Build output record from parsed fields (original logic preserved) MOVE SPACES TO WS-OUT-LINE. STRING WS-FIELD1 DELIMITED BY SPACES '|' WS-FIELD2 DELIMITED BY SPACES '|' WS-FIELD3 DELIMITED BY SPACES INTO WS-OUT-LINE END-STRING. MOVE WS-OUT-LINE TO OUT-REC. WRITE OUT-REC. *> FILE STATUS check after EVERY write IF WS-FILE-OUT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: FILE-OUT status=' WS-FILE-OUT-STATUS MOVE 'E' TO WS-SEVERITY ADD 1 TO WS-TOT-ERRORS END-IF. ADD 1 TO WS-REC-COUNT. ADD 1 TO WS-TOT-WRITTEN. *> Combined hash total: sum of all field lengths COMPUTE WS-HASH-FIELD-LEN = FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD1)) + FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD2)) + FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD3)). ADD WS-HASH-FIELD-LEN TO WS-HASH-TOTAL. *> ===== 4000-REPORT ===== 4000-REPORT SECTION. 4000-START. DISPLAY WS-TS-DISPLAY '4000-REPORT: Finalizing error report'. *> Write error report trailer MOVE SPACES TO ERR-REC. MOVE '--- End of CSV Error Detail Report ---' TO ERR-REC. WRITE ERR-REC. IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF. *> Write error summary to error report MOVE SPACES TO ERR-REC. WRITE ERR-REC. IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF. MOVE WS-LINE-COUNT TO WS-RPT-COUNT. STRING 'Total input lines: ' WS-RPT-COUNT INTO ERR-REC. WRITE ERR-REC. IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF. MOVE WS-MALFORMED-CNT TO WS-RPT-COUNT. STRING 'Malformed records: ' WS-RPT-COUNT INTO ERR-REC. WRITE ERR-REC. IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF. MOVE WS-TRUNC-CNT TO WS-RPT-COUNT. STRING 'Truncation warns: ' WS-RPT-COUNT INTO ERR-REC. WRITE ERR-REC. IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF. MOVE WS-FIELD-COUNT-ERR TO WS-RPT-COUNT. STRING 'Field count errors: ' WS-RPT-COUNT INTO ERR-REC. WRITE ERR-REC. IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF. *> Record count reconciliation summary IF WS-LINE-COUNT = WS-REC-COUNT MOVE 'PASS' TO WS-RPT-LINE ELSE MOVE 'MISMATCH (see audit)' TO WS-RPT-LINE MOVE 'N' TO WS-RECONCILE-OK END-IF. STRING 'Count reconcile: ' WS-RPT-LINE INTO ERR-REC. WRITE ERR-REC. IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF. *> Close error report with STATUS check CLOSE ERROR-REPORT. IF WS-ERR-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' CLOSE ERROR: ERROR-REPORT status=' WS-ERR-STATUS END-IF. DISPLAY WS-TS-DISPLAY ' 4000-REPORT: Error report closed'. *> ===== 5000-AUDIT ===== 5000-AUDIT SECTION. 5000-START. DISPLAY WS-TS-DISPLAY '5000-AUDIT: audit report and control totals'. *> Audit header MOVE '=== CSV Import Summary ===' TO AUD-REC. WRITE AUD-REC. IF WS-AUDIT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: AUDIT-FILE status=' WS-AUDIT-STATUS END-IF. MOVE SPACES TO AUD-REC. WRITE AUD-REC. IF WS-AUDIT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: AUDIT-FILE status=' WS-AUDIT-STATUS END-IF. *> Batch run identification STRING 'Program: CsvFbNoLf' INTO AUD-REC. WRITE AUD-REC. IF WS-AUDIT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: AUDIT-FILE status=' WS-AUDIT-STATUS END-IF. STRING 'Run date: ' WS-CUR-DATE-DISP INTO AUD-REC. WRITE AUD-REC. IF WS-AUDIT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: AUDIT-FILE status=' WS-AUDIT-STATUS END-IF. STRING 'Run time: ' WS-CUR-TIME-DISP INTO AUD-REC. WRITE AUD-REC. IF WS-AUDIT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: AUDIT-FILE status=' WS-AUDIT-STATUS END-IF. MOVE SPACES TO AUD-REC. WRITE AUD-REC. *> Record counts MOVE WS-LINE-COUNT TO WS-RPT-COUNT. STRING 'Input lines read: ' WS-RPT-COUNT INTO AUD-REC. WRITE AUD-REC. MOVE WS-REC-COUNT TO WS-RPT-COUNT. STRING 'Output records written: ' WS-RPT-COUNT INTO AUD-REC. WRITE AUD-REC. *> Record count reconciliation IF WS-LINE-COUNT = WS-REC-COUNT MOVE 'PASS' TO WS-RPT-LINE ELSE MOVE 'MISMATCH (see details)' TO WS-RPT-LINE MOVE 'N' TO WS-RECONCILE-OK END-IF. STRING 'Line vs record reconcile: ' WS-RPT-LINE INTO AUD-REC. WRITE AUD-REC. *> Error counts MOVE WS-MALFORMED-CNT TO WS-RPT-COUNT. STRING 'Malformed records: ' WS-RPT-COUNT INTO AUD-REC. WRITE AUD-REC. MOVE WS-TRUNC-CNT TO WS-RPT-COUNT. STRING 'Truncation warnings: ' WS-RPT-COUNT INTO AUD-REC. WRITE AUD-REC. MOVE WS-FIELD-COUNT-ERR TO WS-RPT-COUNT. STRING 'Field count errors: ' WS-RPT-COUNT INTO AUD-REC. WRITE AUD-REC. MOVE WS-TOT-QUOTE-ERR TO WS-RPT-COUNT. STRING 'Quote errors: ' WS-RPT-COUNT INTO AUD-REC. WRITE AUD-REC. MOVE SPACES TO AUD-REC. WRITE AUD-REC. *> ------------------------ *> Batch Control Totals *> ------------------------ MOVE '=== Batch Control Totals ===' TO AUD-REC. WRITE AUD-REC. MOVE WS-REC-COUNT TO WS-RPT-COUNT. STRING 'Record count total: ' WS-RPT-COUNT INTO AUD-REC. WRITE AUD-REC. MOVE WS-HASH-FIELD1 TO WS-RPT-COUNT9. STRING 'Hash total Field1: ' WS-RPT-COUNT9 INTO AUD-REC. WRITE AUD-REC. MOVE WS-HASH-FIELD2 TO WS-RPT-COUNT9. STRING 'Hash total Field2: ' WS-RPT-COUNT9 INTO AUD-REC. WRITE AUD-REC. MOVE WS-HASH-FIELD3 TO WS-RPT-COUNT9. STRING 'Hash total Field3: ' WS-RPT-COUNT9 INTO AUD-REC. WRITE AUD-REC. MOVE WS-HASH-TOTAL TO WS-RPT-COUNT9. STRING 'Combined hash total: ' WS-RPT-COUNT9 INTO AUD-REC. WRITE AUD-REC. MOVE SPACES TO AUD-REC. WRITE AUD-REC. *> Processing success rate IF WS-LINE-COUNT > 0 COMPUTE WS-RPT-PCT ROUNDED = (WS-REC-COUNT / WS-LINE-COUNT) * 100 STRING 'Processing success rate: ' WS-RPT-PCT '%' INTO AUD-REC WRITE AUD-REC IF WS-AUDIT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: AUDIT-FILE status=' WS-AUDIT-STATUS END-IF END-IF. MOVE SPACES TO AUD-REC. WRITE AUD-REC. *> Audit trailer MOVE '=== End of CSV Audit Report ===' TO AUD-REC. WRITE AUD-REC. IF WS-AUDIT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' WRITE ERROR: AUDIT-FILE status=' WS-AUDIT-STATUS END-IF. *> Close audit file with STATUS check CLOSE AUDIT-FILE. IF WS-AUDIT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' CLOSE ERROR: AUDIT-FILE status=' WS-AUDIT-STATUS END-IF. DISPLAY WS-TS-DISPLAY ' 5000-AUDIT: Audit file closed'. *> ===== 6000-ERROR-HANDLE ===== 6000-ERROR-HANDLE SECTION. 6000-START. DISPLAY WS-TS-DISPLAY ' 6000-ERROR-HANDLE: Error summary'. *> FILE-IN status summary IF WS-FILE-IN-STATUS NOT = '00' AND NOT = '10' DISPLAY WS-TS-DISPLAY ' FILE-IN status: ' WS-FILE-IN-STATUS ADD 1 TO WS-TOT-ERRORS ELSE DISPLAY WS-TS-DISPLAY ' FILE-IN status OK (' WS-FILE-IN-STATUS ')' END-IF. *> FILE-OUT status summary IF WS-FILE-OUT-STATUS NOT = '00' AND NOT = '10' DISPLAY WS-TS-DISPLAY ' FILE-OUT status: ' WS-FILE-OUT-STATUS ADD 1 TO WS-TOT-ERRORS ELSE DISPLAY WS-TS-DISPLAY ' FILE-OUT status OK (' WS-FILE-OUT-STATUS ')' END-IF. *> ERROR-REPORT status summary IF WS-ERR-STATUS NOT = '00' AND NOT = '10' DISPLAY WS-TS-DISPLAY ' ERROR-REPORT status: ' WS-ERR-STATUS ADD 1 TO WS-TOT-ERRORS ELSE DISPLAY WS-TS-DISPLAY ' ERROR-REPORT status OK (' WS-ERR-STATUS ')' END-IF. *> AUDIT-FILE status summary IF WS-AUDIT-STATUS NOT = '00' AND NOT = '10' DISPLAY WS-TS-DISPLAY ' AUDIT-FILE status: ' WS-AUDIT-STATUS ADD 1 TO WS-TOT-ERRORS ELSE DISPLAY WS-TS-DISPLAY ' AUDIT-FILE status OK (' WS-AUDIT-STATUS ')' END-IF. *> Error count summary with severity breakdown DISPLAY WS-TS-DISPLAY ' Severity totals:' MOVE WS-TOT-WARNINGS TO WS-RPT-COUNT9 DISPLAY ' Warnings: ' WS-RPT-COUNT9 MOVE WS-TOT-ERRORS TO WS-RPT-COUNT9 DISPLAY ' Errors: ' WS-RPT-COUNT9 MOVE WS-TOT-CRITICAL TO WS-RPT-COUNT9 DISPLAY ' Critical: ' WS-RPT-COUNT9. *> Record count reconciliation check IF WS-LINE-COUNT NOT = WS-REC-COUNT DISPLAY WS-TS-DISPLAY ' WARNING: Line/output count mismatch' MOVE 'N' TO WS-RECONCILE-OK END-IF. *> ===== 9000-EXIT ===== 9000-EXIT SECTION. 9000-START. DISPLAY WS-TS-DISPLAY ' 9000-EXIT: Program CsvFbNoLf completed'. *> Final record counts display MOVE WS-LINE-COUNT TO WS-RPT-COUNT. MOVE WS-REC-COUNT TO WS-LINE-DISP. DISPLAY ' Input lines: ' WS-RPT-COUNT. MOVE WS-REC-COUNT TO WS-RPT-COUNT. DISPLAY ' Output recs: ' WS-RPT-COUNT. DISPLAY ' Reconcile: ' WS-RECONCILE-OK. *> Error counts display MOVE WS-MALFORMED-CNT TO WS-RPT-COUNT. DISPLAY ' Malformed: ' WS-RPT-COUNT. MOVE WS-TRUNC-CNT TO WS-RPT-COUNT. DISPLAY ' Truncated: ' WS-RPT-COUNT. MOVE WS-FIELD-COUNT-ERR TO WS-RPT-COUNT. DISPLAY ' Field err: ' WS-RPT-COUNT. *> Hash total display MOVE WS-HASH-TOTAL TO WS-RPT-COUNT9. DISPLAY ' Hash total: ' WS-RPT-COUNT9. *> Output file listing DISPLAY ' Output file: file-out.dat'. DISPLAY ' Error report: csv-errors.dat'. DISPLAY ' Audit report: csv-audit.dat'. *> Close FILE-OUT with STATUS check CLOSE FILE-OUT. IF WS-FILE-OUT-STATUS NOT = '00' DISPLAY WS-TS-DISPLAY ' CLOSE ERROR: FILE-OUT status=' WS-FILE-OUT-STATUS END-IF. DISPLAY WS-TS-DISPLAY ' FILE-OUT closed, status=' WS-FILE-OUT-STATUS. *> Set return code based on reconciliation status IF WS-RECONCILE-OK = 'N' MOVE 4 TO RETURN-CODE DISPLAY WS-TS-DISPLAY ' RETURN-CODE=4 (reconciliation mismatch)' ELSE MOVE 0 TO RETURN-CODE DISPLAY WS-TS-DISPLAY ' RETURN-CODE=0 (normal completion)' END-IF. *> Terminate program STOP RUN. *> ===== PARSE-CSV-LINE (original logic, UNCHANGED) ===== PARSE-CSV-LINE. MOVE SPACES TO WS-FIELDS. MOVE 1 TO WS-POS. MOVE 1 TO WS-FIELD-NUM. MOVE 1 TO WS-CHAR-OFFSET. MOVE 'N' TO WS-IN-QUOTE. PERFORM VARYING WS-POS FROM 1 BY 1 UNTIL WS-POS > 200 OR WS-FIELD-NUM > 3 MOVE IN-LINE(WS-POS:1) TO WS-CHAR IF WS-CHAR = '"' IF WS-IN-QUOTE-YES MOVE 'N' TO WS-IN-QUOTE ELSE MOVE 'Y' TO WS-IN-QUOTE END-IF ELSE IF WS-CHAR = ',' AND NOT WS-IN-QUOTE-YES ADD 1 TO WS-FIELD-NUM MOVE 1 TO WS-CHAR-OFFSET ELSE IF WS-CHAR NOT = SPACE OR WS-CHAR-OFFSET > 1 THEN PERFORM SAVE-CHAR-TO-FIELD END-IF IF WS-CHAR-OFFSET < 30 ADD 1 TO WS-CHAR-OFFSET END-IF END-IF END-IF END-PERFORM. *> ===== SAVE-CHAR-TO-FIELD (original, UNCHANGED) ===== SAVE-CHAR-TO-FIELD. EVALUATE WS-FIELD-NUM WHEN 1 MOVE WS-CHAR TO WS-FIELD1(WS-CHAR-OFFSET:1) WHEN 2 MOVE WS-CHAR TO WS-FIELD2(WS-CHAR-OFFSET:1) WHEN 3 MOVE WS-CHAR TO WS-FIELD3(WS-CHAR-OFFSET:1) END-EVALUATE. END PROGRAM CsvFbNoLf.