*> ============================================================ *> 32-mix-1N-samekeybreak : 合同+月别集计 (Contract+Monthly) *> Input : FILE-MAST (master.dat: 合同), FILE-DETL (detail.dat: CDR) *> Output: FILE-OUT (match-output.txt: 合同别月集计结果) *> AUDIT-OUT (audit-32.log: 审计跟踪) *> Coverage: AM-N006, MT-R001 *> Version : 2.0 — Expanded with audit, hash totals, FILE STATUS *> ============================================================ IDENTIFICATION DIVISION. PROGRAM-ID. Main32Mix1NSameKeyBreak. ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT FILE-MAST ASSIGN TO "master.dat" ORGANIZATION IS LINE SEQUENTIAL FILE STATUS IS FS-MAST. SELECT FILE-DETL ASSIGN TO "detail.dat" ORGANIZATION IS LINE SEQUENTIAL FILE STATUS IS FS-DETL. SELECT FILE-OUT ASSIGN TO "match-output.txt" ORGANIZATION IS LINE SEQUENTIAL FILE STATUS IS FS-OUT. SELECT AUDIT-OUT ASSIGN TO "audit-32.log" ORGANIZATION IS LINE SEQUENTIAL FILE STATUS IS FS-AUDIT. DATA DIVISION. FILE SECTION. FD FILE-MAST. 01 MAST-REC. 05 MAST-KEY PIC X(10). 05 MAST-NAME PIC X(20). 05 MAST-LIMIT PIC 9(07). FD FILE-DETL. 01 DETL-REC. 05 DETL-KEY PIC X(10). 05 DETL-ITEM PIC X(15). 05 DETL-AMOUNT PIC 9(07). FD FILE-OUT. 01 OUT-REC. 05 OUT-LINE PIC X(80). FD AUDIT-OUT. 01 AUDIT-REC. 05 AUDIT-LINE PIC X(120). WORKING-STORAGE SECTION. 01 WS-TELECOM-REC. COPY "telecom/TEL-BILLING.cpy". *> ============================================================ *> FILE STATUS fields *> ============================================================ 01 FS-MAST PIC X(02) VALUE "00". 01 FS-DETL PIC X(02) VALUE "00". 01 FS-OUT PIC X(02) VALUE "00". 01 FS-AUDIT PIC X(02) VALUE "00". *> ============================================================ *> End-of-file flags *> ============================================================ 01 WS-EOF-MAST PIC X(01) VALUE "N". 88 MAST-EOF VALUE "Y". 01 WS-EOF-DETL PIC X(01) VALUE "N". 88 DETL-EOF VALUE "Y". *> ============================================================ *> Key break fields (same-key: contract key) *> ============================================================ 01 WS-MASTER-FOUND PIC X(01) VALUE "N". 01 WS-MAST-KEY PIC X(10). 01 WS-PREV-KEY PIC X(10). 01 WS-KEY-BREAK PIC X(01) VALUE "N". 88 KEY-BREAK VALUE "Y". *> ============================================================ *> Core counters *> ============================================================ 01 WS-MATCH-COUNT PIC 9(05) VALUE 0. 01 WS-BREAK-COUNT PIC 9(05) VALUE 0. 01 WS-BREAK-TOTAL PIC 9(09) VALUE 0. 01 WS-GRAND-TOTAL PIC 9(09) VALUE 0. 01 WS-DETAIL-COUNT PIC 9(05) VALUE 0. *> ============================================================ *> Expanded counters: match path, unmatched path, break path *> ============================================================ 01 WS-MATCH-PATH-CNT PIC 9(05) VALUE 0. 01 WS-UNMATCH-CNT PIC 9(05) VALUE 0. 01 WS-BREAK-PATH-CNT PIC 9(05) VALUE 0. 01 WS-VALID-COUNT PIC 9(05) VALUE 0. 01 WS-INVALID-COUNT PIC 9(05) VALUE 0. 01 WS-TOTAL-RECORDS PIC 9(05) VALUE 0. *> ============================================================ *> Hash totals per category *> ============================================================ 01 WS-HASH-MATCHED PIC 9(11) VALUE 0. 01 WS-HASH-UNMATCHED PIC 9(11) VALUE 0. 01 WS-HASH-BREAK PIC 9(11) VALUE 0. 01 WS-HASH-GRAND PIC 9(11) VALUE 0. *> ============================================================ *> Master lookup table (loaded into memory) *> ============================================================ 01 MASTER-TABLE. 05 MASTER-ENTRY OCCURS 10 TIMES. 10 ME-KEY PIC X(10). 10 ME-NAME PIC X(20). 10 ME-LIMIT PIC 9(07). 01 IDX PIC 9(02). 01 MAST-IDX PIC 9(02). 01 WS-MAST-COUNT PIC 9(02). 01 WS-MASTER-NAME PIC X(20). 01 WS-MASTER-LIMIT PIC 9(07). 01 WS-MASTER-FOUND-IDX PIC 9(02). *> ============================================================ *> Timestamp and batch control *> ============================================================ 01 WS-TIMESTAMP. 05 WS-TS-DATE PIC X(08). 05 FILLER PIC X(01) VALUE SPACE. 05 WS-TS-TIME PIC X(08). 01 WS-BATCH-ID PIC X(08) VALUE "BATCH032". 01 WS-BATCH-START PIC X(16). 01 WS-BATCH-END PIC X(16). 01 WS-PROGRAM-NAME PIC X(30) VALUE "Main32Mix1NSameKeyBreak". *> ============================================================ *> Error severity levels *> ============================================================ 01 WS-ERROR-SEVERITY. 05 WS-ERR-LEVEL PIC X(01). 88 ERR-INFO VALUE "I". 88 ERR-WARN VALUE "W". 88 ERR-ERROR VALUE "E". 88 ERR-CRITICAL VALUE "C". 01 WS-ERROR-MESSAGE PIC X(80). 01 WS-ERR-COUNT-INFO PIC 9(03) VALUE 0. 01 WS-ERR-COUNT-WARN PIC 9(03) VALUE 0. 01 WS-ERR-COUNT-ERROR PIC 9(03) VALUE 0. 01 WS-ERR-COUNT-CRIT PIC 9(03) VALUE 0. *> ============================================================ *> FILE STATUS trace buffer *> ============================================================ 01 WS-FS-TRACE PIC X(40). 01 WS-FS-EXPECTED PIC X(02) VALUE "00". *> ============================================================ *> Audit line templates *> ============================================================ 01 WS-AUDIT-HEADER. 05 FILLER PIC X(08) VALUE "AUDIT ". 05 FILLER PIC X(01) VALUE SPACE. 05 AH-PGM PIC X(30). 05 FILLER PIC X(01) VALUE SPACE. 05 AH-BATCH PIC X(08). 05 FILLER PIC X(01) VALUE SPACE. 05 AH-TIMESTAMP PIC X(17). 01 WS-AUDIT-ENTRY. 05 FILLER PIC X(08) VALUE " ENTRY ". 05 AE-TEXT PIC X(72). 01 WS-AUDIT-STATS. 05 FILLER PIC X(08) VALUE " STATS ". 05 AS-TEXT PIC X(72). 01 WS-AUDIT-ERROR. 05 FILLER PIC X(08) VALUE " ERROR ". 05 AE-LEVEL PIC X(01). 05 FILLER PIC X(01) VALUE SPACE. 05 AE-MSG PIC X(70). *> ============================================================ *> Output line templates (preserved from original) *> ============================================================ 01 WS-HEADER-LINE. 05 FILLER PIC X(40) VALUE "Master Key Name ". 05 FILLER PIC X(40) VALUE "Detail Item Amount". 01 WS-MATCH-LINE. 05 FILLER PIC X(02) VALUE " ". 05 ML-KEY PIC X(10). 05 FILLER PIC X(02) VALUE SPACES. 05 ML-NAME PIC X(20). 05 FILLER PIC X(02) VALUE SPACES. 05 ML-ITEM PIC X(15). 05 FILLER PIC X(02) VALUE SPACES. 05 ML-AMOUNT PIC Z(9)9. 01 WS-BREAK-LINE. 05 FILLER PIC X(10) VALUE "*** BREAK ". 05 BL-KEY PIC X(10). 05 FILLER PIC X(10) VALUE " Count: ". 05 BL-COUNT PIC Z(9). 05 FILLER PIC X(10) VALUE " Total: ". 05 BL-TOTAL PIC Z(9)9. 01 WS-UNMATCHED-LINE. 05 FILLER PIC X(20) VALUE " UNMATCHED detail: ". 05 UL-KEY PIC X(10). *> ============================================================ *> Report totals template *> ============================================================ 01 WS-REPORT-LINE. 05 FILLER PIC X(02) VALUE SPACES. 05 RL-LABEL PIC X(30). 05 FILLER PIC X(02) VALUE SPACES. 05 RL-VALUE PIC Z(9)9. 01 WS-HASH-LINE. 05 FILLER PIC X(02) VALUE SPACES. 05 HL-LABEL PIC X(30). 05 FILLER PIC X(02) VALUE SPACES. 05 HL-VALUE PIC Z(10)9. *> ============================================================ *> Temporary work fields *> ============================================================ 01 WS-TEMP-AMOUNT PIC 9(09). 01 WS-TEMP-COUNT PIC 9(05). 01 WS-TEMP-HASH PIC 9(11). PROCEDURE DIVISION. MAIN-PROCEDURE. PERFORM 1000-INIT. PERFORM 2000-OPEN-FILES. PERFORM 3000-PROCESS. PERFORM 4000-REPORT. PERFORM 5000-AUDIT. PERFORM 9000-EXIT. STOP RUN. *> ============================================================ *> 1000-INIT : Initialise batch, timestamp, counters *> ============================================================ 1000-INIT. MOVE FUNCTION CURRENT-DATE (1:8) TO WS-TS-DATE. MOVE FUNCTION CURRENT-DATE (9:8) TO WS-TS-TIME. MOVE WS-TS-DATE TO WS-BATCH-START(1:8). MOVE WS-TS-TIME TO WS-BATCH-START(9:8). MOVE 0 TO WS-MATCH-COUNT. MOVE 0 TO WS-MATCH-PATH-CNT. MOVE 0 TO WS-UNMATCH-CNT. MOVE 0 TO WS-BREAK-PATH-CNT. MOVE 0 TO WS-BREAK-COUNT. MOVE 0 TO WS-VALID-COUNT. MOVE 0 TO WS-INVALID-COUNT. MOVE 0 TO WS-TOTAL-RECORDS. MOVE 0 TO WS-GRAND-TOTAL. MOVE 0 TO WS-HASH-MATCHED. MOVE 0 TO WS-HASH-UNMATCHED. MOVE 0 TO WS-HASH-BREAK. MOVE 0 TO WS-HASH-GRAND. MOVE 0 TO WS-ERR-COUNT-INFO. MOVE 0 TO WS-ERR-COUNT-WARN. MOVE 0 TO WS-ERR-COUNT-ERROR. MOVE 0 TO WS-ERR-COUNT-CRIT. DISPLAY "[" WS-TS-DATE " " WS-TS-TIME "] " WS-PROGRAM-NAME " starting, batch=" WS-BATCH-ID. *> ============================================================ *> 2000-OPEN-FILES : Open all files with FILE STATUS checks *> ============================================================ 2000-OPEN-FILES. OPEN OUTPUT FILE-OUT. MOVE FS-OUT TO WS-FS-TRACE. IF FS-OUT NOT = "00" MOVE "E" TO WS-ERR-LEVEL STRING "FILE-OUT open failed FS=" FS-OUT INTO WS-ERROR-MESSAGE PERFORM 6000-ERROR-HANDLE END-IF. OPEN OUTPUT AUDIT-OUT. MOVE FS-AUDIT TO WS-FS-TRACE. IF FS-AUDIT NOT = "00" MOVE "E" TO WS-ERR-LEVEL STRING "AUDIT-OUT open failed FS=" FS-AUDIT INTO WS-ERROR-MESSAGE PERFORM 6000-ERROR-HANDLE END-IF. MOVE SPACES TO WS-AUDIT-HEADER. MOVE WS-PROGRAM-NAME TO AH-PGM. MOVE WS-BATCH-ID TO AH-BATCH. STRING WS-TS-DATE " " WS-TS-TIME DELIMITED BY SIZE INTO AH-TIMESTAMP. MOVE WS-AUDIT-HEADER TO AUDIT-REC. WRITE AUDIT-REC. MOVE FS-AUDIT TO WS-FS-TRACE. IF FS-AUDIT NOT = "00" DISPLAY "Warning: AUDIT write failed FS=" FS-AUDIT END-IF. *> Load master table into memory PERFORM LOAD-MASTER-TABLE. MOVE "1:N Match with Same-Key Break" TO OUT-REC. WRITE OUT-REC. MOVE SPACES TO OUT-REC. WRITE OUT-REC. MOVE WS-HEADER-LINE TO OUT-REC. WRITE OUT-REC. MOVE SPACES TO OUT-REC. WRITE OUT-REC. MOVE SPACES TO WS-AUDIT-ENTRY. STRING "Header written, master table size=" WS-MAST-COUNT DELIMITED BY SIZE INTO AE-TEXT. MOVE WS-AUDIT-ENTRY TO AUDIT-REC. WRITE AUDIT-REC. *> ============================================================ *> 3000-PROCESS : Main processing loop *> ============================================================ 3000-PROCESS. *> Process: for each detail, find matching master OPEN INPUT FILE-DETL. IF FS-DETL NOT = "00" MOVE "E" TO WS-ERR-LEVEL STRING "FILE-DETL open failed FS=" FS-DETL INTO WS-ERROR-MESSAGE PERFORM 6000-ERROR-HANDLE END-IF. MOVE 'N' TO WS-EOF-DETL. MOVE 0 TO WS-MATCH-COUNT. MOVE 0 TO WS-BREAK-COUNT. MOVE 0 TO WS-GRAND-TOTAL. MOVE SPACES TO WS-PREV-KEY. MOVE "I" TO WS-ERR-LEVEL. MOVE "Detail processing started" TO WS-ERROR-MESSAGE. PERFORM 6000-ERROR-HANDLE. PERFORM UNTIL DETL-EOF READ FILE-DETL INTO DETL-REC AT END SET DETL-EOF TO TRUE PERFORM HANDLE-FINAL-BREAK NOT AT END PERFORM PROCESS-DETAIL END-READ MOVE FS-DETL TO WS-FS-TRACE IF NOT DETL-EOF AND FS-DETL NOT = "00" MOVE "E" TO WS-ERR-LEVEL STRING "FILE-DETL read failed FS=" FS-DETL INTO WS-ERROR-MESSAGE PERFORM 6000-ERROR-HANDLE END-IF END-PERFORM. CLOSE FILE-DETL. IF FS-DETL NOT = "00" MOVE "W" TO WS-ERR-LEVEL STRING "FILE-DETL close FS=" FS-DETL INTO WS-ERROR-MESSAGE PERFORM 6000-ERROR-HANDLE END-IF. *> ============================================================ *> 3100-VALIDATE : Validate detail record fields *> ============================================================ 3100-VALIDATE. *> Called from PROCESS-DETAIL for each record ADD 1 TO WS-TOTAL-RECORDS. *> Validate key not empty IF DETL-KEY = SPACES OR ZERO ADD 1 TO WS-INVALID-COUNT MOVE "W" TO WS-ERR-LEVEL STRING "Empty key detected at record " WS-TOTAL-RECORDS INTO WS-ERROR-MESSAGE PERFORM 6000-ERROR-HANDLE EXIT PARAGRAPH END-IF. *> Validate amount within range IF DETL-AMOUNT = 0 ADD 1 TO WS-INVALID-COUNT MOVE "I" TO WS-ERR-LEVEL STRING "Zero amount for key " DETL-KEY INTO WS-ERROR-MESSAGE PERFORM 6000-ERROR-HANDLE END-IF. *> Validate amount not exceeding reasonable threshold IF DETL-AMOUNT > 9999999 ADD 1 TO WS-INVALID-COUNT MOVE "W" TO WS-ERR-LEVEL STRING "Suspicious large amount " DETL-AMOUNT " for key " DETL-KEY INTO WS-ERROR-MESSAGE PERFORM 6000-ERROR-HANDLE END-IF. ADD 1 TO WS-VALID-COUNT. *> ============================================================ *> 3200-CALCULATE : Accumulate hash totals and running totals *> ============================================================ 3200-CALCULATE. *> Called from PROCESS-DETAIL after match determination *> Accumulate hash totals per category IF WS-MASTER-FOUND = "Y" ADD DETL-AMOUNT TO WS-HASH-MATCHED ELSE ADD DETL-AMOUNT TO WS-HASH-UNMATCHED END-IF. ADD DETL-AMOUNT TO WS-HASH-GRAND. *> Cross-check: hash grand should equal matched + unmatched COMPUTE WS-TEMP-HASH = WS-HASH-MATCHED + WS-HASH-UNMATCHED. IF WS-TEMP-HASH NOT = WS-HASH-GRAND MOVE "W" TO WS-ERR-LEVEL STRING "Hash cross-check mismatch: " WS-HASH-GRAND " vs " WS-TEMP-HASH INTO WS-ERROR-MESSAGE PERFORM 6000-ERROR-HANDLE END-IF. *> ============================================================ *> 3300-FORMAT-OUTPUT : Format output line with tracing *> ============================================================ 3300-FORMAT-OUTPUT. *> Called from PROCESS-DETAIL *> (formatting is done inline in PROCESS-DETAIL using *> existing WS-MATCH-LINE / WS-UNMATCHED-LINE templates) *> This paragraph logs output to the audit trail. MOVE SPACES TO WS-AUDIT-ENTRY. IF WS-MASTER-FOUND = "Y" STRING "MATCH key=" DETL-KEY " item=" DETL-ITEM " amt=" DETL-AMOUNT DELIMITED BY SIZE INTO AE-TEXT ELSE STRING "UNMATCH key=" DETL-KEY " item=" DETL-ITEM " amt=" DETL-AMOUNT DELIMITED BY SIZE INTO AE-TEXT END-IF. MOVE WS-AUDIT-ENTRY TO AUDIT-REC. WRITE AUDIT-REC. *> ============================================================ *> 3400-WRITE-OUTPUT : Write record to output + FILE STATUS check *> ============================================================ 3400-WRITE-OUTPUT. *> Called after each WRITE to the output file *> Performs FILE STATUS check and audit logging. MOVE FS-OUT TO WS-FS-TRACE. IF FS-OUT NOT = "00" MOVE "E" TO WS-ERR-LEVEL STRING "FILE-OUT write failed FS=" FS-OUT INTO WS-ERROR-MESSAGE PERFORM 6000-ERROR-HANDLE END-IF. *> ============================================================ *> 4000-REPORT : Write summary report to output and audit *> ============================================================ 4000-REPORT. MOVE SPACES TO OUT-REC. WRITE OUT-REC. PERFORM 3400-WRITE-OUTPUT. MOVE SPACES TO WS-REPORT-LINE. MOVE "Total matches:" TO RL-LABEL. MOVE WS-MATCH-COUNT TO RL-VALUE. MOVE WS-REPORT-LINE TO OUT-REC. WRITE OUT-REC. PERFORM 3400-WRITE-OUTPUT. MOVE SPACES TO WS-REPORT-LINE. MOVE "Total unmatched:" TO RL-LABEL. MOVE WS-UNMATCH-CNT TO RL-VALUE. MOVE WS-REPORT-LINE TO OUT-REC. WRITE OUT-REC. PERFORM 3400-WRITE-OUTPUT. MOVE SPACES TO WS-REPORT-LINE. MOVE "Total breaks:" TO RL-LABEL. MOVE WS-BREAK-COUNT TO RL-VALUE. MOVE WS-REPORT-LINE TO OUT-REC. WRITE OUT-REC. PERFORM 3400-WRITE-OUTPUT. MOVE SPACES TO WS-REPORT-LINE. MOVE "Valid records:" TO RL-LABEL. MOVE WS-VALID-COUNT TO RL-VALUE. MOVE WS-REPORT-LINE TO OUT-REC. WRITE OUT-REC. PERFORM 3400-WRITE-OUTPUT. MOVE SPACES TO WS-REPORT-LINE. MOVE "Invalid records:" TO RL-LABEL. MOVE WS-INVALID-COUNT TO RL-VALUE. MOVE WS-REPORT-LINE TO OUT-REC. WRITE OUT-REC. PERFORM 3400-WRITE-OUTPUT. MOVE SPACES TO WS-HASH-LINE. MOVE "Hash total (matched):" TO HL-LABEL. MOVE WS-HASH-MATCHED TO HL-VALUE. MOVE WS-HASH-LINE TO OUT-REC. WRITE OUT-REC. PERFORM 3400-WRITE-OUTPUT. MOVE SPACES TO WS-HASH-LINE. MOVE "Hash total (unmatched):" TO HL-LABEL. MOVE WS-HASH-UNMATCHED TO HL-VALUE. MOVE WS-HASH-LINE TO OUT-REC. WRITE OUT-REC. PERFORM 3400-WRITE-OUTPUT. MOVE SPACES TO WS-HASH-LINE. MOVE "Hash total (grand):" TO HL-LABEL. MOVE WS-HASH-GRAND TO HL-VALUE. MOVE WS-HASH-LINE TO OUT-REC. WRITE OUT-REC. PERFORM 3400-WRITE-OUTPUT. STRING "Total matches: " WS-MATCH-COUNT " Breaks: " WS-BREAK-COUNT " Grand total: " WS-GRAND-TOTAL DELIMITED BY SIZE INTO OUT-REC. WRITE OUT-REC. PERFORM 3400-WRITE-OUTPUT. MOVE SPACES TO WS-AUDIT-ENTRY. STRING "REPORT: match=" WS-MATCH-COUNT " unmatched=" WS-UNMATCH-CNT " break=" WS-BREAK-COUNT " valid=" WS-VALID-COUNT " invalid=" WS-INVALID-COUNT " grand=" WS-GRAND-TOTAL " hashG=" WS-HASH-GRAND DELIMITED BY SIZE INTO AE-TEXT. MOVE WS-AUDIT-ENTRY TO AUDIT-REC. WRITE AUDIT-REC. MOVE SPACES TO WS-AUDIT-ENTRY. STRING "ERRORS: info=" WS-ERR-COUNT-INFO " warn=" WS-ERR-COUNT-WARN " error=" WS-ERR-COUNT-ERROR " crit=" WS-ERR-COUNT-CRIT DELIMITED BY SIZE INTO AE-TEXT. MOVE WS-AUDIT-ENTRY TO AUDIT-REC. WRITE AUDIT-REC. *> ============================================================ *> 5000-AUDIT : Write final audit summary, close audit file *> ============================================================ 5000-AUDIT. MOVE FUNCTION CURRENT-DATE (1:8) TO WS-TS-DATE. MOVE FUNCTION CURRENT-DATE (9:8) TO WS-TS-TIME. MOVE WS-TS-DATE TO WS-BATCH-END(1:8). MOVE WS-TS-TIME TO WS-BATCH-END(9:8). MOVE SPACES TO WS-AUDIT-STATS. STRING "Batch " WS-BATCH-ID " ended " WS-BATCH-END DELIMITED BY SIZE INTO AS-TEXT. MOVE WS-AUDIT-STATS TO AUDIT-REC. WRITE AUDIT-REC. MOVE SPACES TO WS-AUDIT-STATS. STRING "Records total=" WS-TOTAL-RECORDS " match=" WS-MATCH-PATH-CNT " break=" WS-BREAK-PATH-CNT DELIMITED BY SIZE INTO AS-TEXT. MOVE WS-AUDIT-STATS TO AUDIT-REC. WRITE AUDIT-REC. MOVE SPACES TO WS-AUDIT-STATS. STRING "Hash matched=" WS-HASH-MATCHED " unmatched=" WS-HASH-UNMATCHED " grand=" WS-HASH-GRAND DELIMITED BY SIZE INTO AS-TEXT. MOVE WS-AUDIT-STATS TO AUDIT-REC. WRITE AUDIT-REC. CLOSE AUDIT-OUT. IF FS-AUDIT NOT = "00" DISPLAY "Warning: AUDIT close FS=" FS-AUDIT END-IF. *> ============================================================ *> LOAD-MASTER-TABLE (original logic preserved, expanded) *> ============================================================ LOAD-MASTER-TABLE. OPEN INPUT FILE-MAST. IF FS-MAST NOT = "00" MOVE "E" TO WS-ERR-LEVEL STRING "FILE-MAST open failed FS=" FS-MAST INTO WS-ERROR-MESSAGE PERFORM 6000-ERROR-HANDLE END-IF. MOVE 0 TO WS-MAST-COUNT. PERFORM VARYING IDX FROM 1 BY 1 UNTIL IDX > 10 READ FILE-MAST INTO MAST-REC AT END EXIT PERFORM END-READ MOVE FS-MAST TO WS-FS-TRACE IF FS-MAST NOT = "00" AND FS-MAST NOT = "10" MOVE "W" TO WS-ERR-LEVEL STRING "FILE-MAST read idx=" IDX " FS=" FS-MAST INTO WS-ERROR-MESSAGE PERFORM 6000-ERROR-HANDLE END-IF MOVE MAST-KEY TO ME-KEY(IDX) MOVE MAST-NAME TO ME-NAME(IDX) MOVE MAST-LIMIT TO ME-LIMIT(IDX) ADD 1 TO WS-MAST-COUNT END-PERFORM. CLOSE FILE-MAST. MOVE FS-MAST TO WS-FS-TRACE. IF FS-MAST NOT = "00" MOVE "W" TO WS-ERR-LEVEL STRING "FILE-MAST close FS=" FS-MAST INTO WS-ERROR-MESSAGE PERFORM 6000-ERROR-HANDLE END-IF. DISPLAY "Master table loaded: " WS-MAST-COUNT " entries". MOVE SPACES TO WS-AUDIT-ENTRY. STRING "LOAD-MASTER: " WS-MAST-COUNT " entries loaded" DELIMITED BY SIZE INTO AE-TEXT. MOVE WS-AUDIT-ENTRY TO AUDIT-REC. WRITE AUDIT-REC. *> ============================================================ *> PROCESS-DETAIL (original logic preserved, expanded) *> ============================================================ PROCESS-DETAIL. *> Run validation PERFORM 3100-VALIDATE. *> Check for key break (same key) IF DETL-KEY NOT = WS-PREV-KEY IF WS-PREV-KEY NOT = SPACES ADD 1 TO WS-BREAK-PATH-CNT PERFORM HANDLE-KEY-BREAK END-IF MOVE DETL-KEY TO WS-PREV-KEY MOVE 0 TO WS-DETAIL-COUNT MOVE 0 TO WS-BREAK-TOTAL MOVE SPACES TO WS-AUDIT-ENTRY STRING "KEYBREAK: new key=" DETL-KEY DELIMITED BY SIZE INTO AE-TEXT MOVE WS-AUDIT-ENTRY TO AUDIT-REC WRITE AUDIT-REC END-IF. *> Find matching master MOVE "N" TO WS-MASTER-FOUND. MOVE 0 TO WS-MASTER-FOUND-IDX. PERFORM VARYING MAST-IDX FROM 1 BY 1 UNTIL MAST-IDX > WS-MAST-COUNT IF ME-KEY(MAST-IDX) = DETL-KEY MOVE "Y" TO WS-MASTER-FOUND MOVE ME-NAME(MAST-IDX) TO WS-MASTER-NAME MOVE ME-LIMIT(MAST-IDX) TO WS-MASTER-LIMIT MOVE MAST-IDX TO WS-MASTER-FOUND-IDX END-IF END-PERFORM. *> Perform hash accumulation (3200) PERFORM 3200-CALCULATE. IF WS-MASTER-FOUND = "Y" ADD 1 TO WS-MATCH-COUNT ADD 1 TO WS-MATCH-PATH-CNT ADD 1 TO WS-DETAIL-COUNT ADD DETL-AMOUNT TO WS-BREAK-TOTAL ADD DETL-AMOUNT TO WS-GRAND-TOTAL MOVE DETL-KEY TO ML-KEY MOVE WS-MASTER-NAME TO ML-NAME MOVE DETL-ITEM TO ML-ITEM MOVE DETL-AMOUNT TO ML-AMOUNT DISPLAY " " WS-MATCH-LINE MOVE WS-MATCH-LINE TO OUT-REC WRITE OUT-REC PERFORM 3400-WRITE-OUTPUT ELSE ADD 1 TO WS-UNMATCH-CNT MOVE DETL-KEY TO UL-KEY DISPLAY " " WS-UNMATCHED-LINE MOVE WS-UNMATCHED-LINE TO OUT-REC WRITE OUT-REC PERFORM 3400-WRITE-OUTPUT END-IF. *> Audit trace for this detail PERFORM 3300-FORMAT-OUTPUT. *> ============================================================ *> HANDLE-KEY-BREAK (original logic preserved, expanded) *> ============================================================ HANDLE-KEY-BREAK. ADD 1 TO WS-BREAK-COUNT. MOVE WS-PREV-KEY TO BL-KEY. MOVE WS-DETAIL-COUNT TO BL-COUNT. MOVE WS-BREAK-TOTAL TO BL-TOTAL. ADD WS-BREAK-TOTAL TO WS-HASH-BREAK. DISPLAY " " WS-BREAK-LINE. MOVE WS-BREAK-LINE TO OUT-REC. WRITE OUT-REC. PERFORM 3400-WRITE-OUTPUT. MOVE SPACES TO WS-AUDIT-ENTRY. STRING "BREAK: key=" WS-PREV-KEY " count=" WS-DETAIL-COUNT " total=" WS-BREAK-TOTAL DELIMITED BY SIZE INTO AE-TEXT. MOVE WS-AUDIT-ENTRY TO AUDIT-REC. WRITE AUDIT-REC. *> ============================================================ *> HANDLE-FINAL-BREAK (original logic preserved, expanded) *> ============================================================ HANDLE-FINAL-BREAK. IF WS-PREV-KEY NOT = SPACES ADD 1 TO WS-BREAK-PATH-CNT PERFORM HANDLE-KEY-BREAK END-IF. MOVE SPACES TO WS-AUDIT-ENTRY. STRING "FINAL-BREAK: total break count=" WS-BREAK-COUNT DELIMITED BY SIZE INTO AE-TEXT. MOVE WS-AUDIT-ENTRY TO AUDIT-REC. WRITE AUDIT-REC. *> ============================================================ *> 6000-ERROR-HANDLE : Centralized error handling by severity *> ============================================================ 6000-ERROR-HANDLE. EVALUATE WS-ERR-LEVEL WHEN "I" ADD 1 TO WS-ERR-COUNT-INFO DISPLAY "INFO: " WS-ERROR-MESSAGE WHEN "W" ADD 1 TO WS-ERR-COUNT-WARN DISPLAY "WARN: " WS-ERROR-MESSAGE WHEN "E" ADD 1 TO WS-ERR-COUNT-ERROR DISPLAY "ERROR: " WS-ERROR-MESSAGE WHEN "C" ADD 1 TO WS-ERR-COUNT-CRIT DISPLAY "CRITICAL: " WS-ERROR-MESSAGE MOVE WS-ERROR-MESSAGE TO OUT-REC WRITE OUT-REC MOVE SPACES TO WS-AUDIT-ERROR MOVE "C" TO AE-LEVEL STRING "ABORT: " WS-ERROR-MESSAGE DELIMITED BY SIZE INTO AE-MSG MOVE WS-AUDIT-ERROR TO AUDIT-REC WRITE AUDIT-REC CLOSE FILE-OUT CLOSE AUDIT-OUT STOP RUN WHEN OTHER DISPLAY "UNKNOWN: " WS-ERROR-MESSAGE END-EVALUATE. *> Write error to audit log MOVE SPACES TO WS-AUDIT-ERROR. MOVE WS-ERR-LEVEL TO AE-LEVEL. MOVE WS-ERROR-MESSAGE TO AE-MSG. MOVE WS-AUDIT-ERROR TO AUDIT-REC. WRITE AUDIT-REC. *> ============================================================ *> 9000-EXIT : Close files and terminate *> ============================================================ 9000-EXIT. MOVE FUNCTION CURRENT-DATE (1:8) TO WS-TS-DATE. MOVE FUNCTION CURRENT-DATE (9:8) TO WS-TS-TIME. MOVE WS-TS-DATE TO WS-BATCH-END(1:8). MOVE WS-TS-TIME TO WS-BATCH-END(9:8). DISPLAY "[" WS-TS-DATE " " WS-TS-TIME "] " WS-PROGRAM-NAME " ending, batch=" WS-BATCH-ID. CLOSE FILE-OUT. IF FS-OUT NOT = "00" DISPLAY "Warning: FILE-OUT close FS=" FS-OUT END-IF. DISPLAY "Output written to match-output.txt". DISPLAY "Audit written to audit-32.log".