Files
cobol-java-v3/benchmark-programs/18-matching-MN-to-M/main-18-matching-MN-to-M.cbl
T
NB-076 94400d50d4 feat: add benchmark-programs — 58 telecom COBOL test programs
作为子目录纳入系统,与核心测试管道协同

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-25 09:53:21 +08:00

833 lines
32 KiB
COBOL

*> ============================================================
*> 18-matching-MN-to-M : 合同↔套餐M:N→M (Contract↔Plan M:N→M)
*> Input : FILE-MASTER (master.dat: 合同), FILE-DETAIL (detail.dat: 套餐)
*> Output: FILE-OUT (output.dat: M条合同记录)
*> Coverage: AM-N003, AM-A002, AM-R001
*>
*> EXPANDED: Added SECTION structure, contract eligibility validation,
*> plan effective date check, duplicate plan assignment detection,
*> audit file, error file, control totals, hash totals, tracing.
*> ============================================================
IDENTIFICATION DIVISION.
PROGRAM-ID. MatchingMNtoM.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT FILE-MASTER ASSIGN TO 'master.dat'
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-FILE-STATUS-1.
SELECT FILE-DETAIL ASSIGN TO 'detail.dat'
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-FILE-STATUS-2.
SELECT FILE-OUT ASSIGN TO 'output.dat'
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-FILE-STATUS-3.
SELECT AUDIT-FILE ASSIGN TO 'audit-report-18.txt'
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-FILE-STATUS-4.
SELECT ERROR-FILE ASSIGN TO 'error-report-18.txt'
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-FILE-STATUS-5.
DATA DIVISION.
FILE SECTION.
FD FILE-MASTER.
01 MASTER-REC.
COPY "STD-REC.cpy".
FD FILE-DETAIL.
01 DETAIL-REC.
COPY "STD-REC.cpy".
FD FILE-OUT.
01 OUT-REC.
COPY "STD-REC.cpy".
FD AUDIT-FILE.
01 AUDIT-LINE PIC X(120).
FD ERROR-FILE.
01 ERROR-LINE PIC X(120).
WORKING-STORAGE SECTION.
01 WS-TELECOM-REC.
COPY "telecom/TEL-INVOICE.cpy".
*> ============================================================
*> FILE STATUS FIELDS
*> ============================================================
01 WS-FILE-STATUS-1 PIC X(02).
01 WS-FILE-STATUS-2 PIC X(02).
01 WS-FILE-STATUS-3 PIC X(02).
01 WS-FILE-STATUS-4 PIC X(02).
01 WS-FILE-STATUS-5 PIC X(02).
*> ============================================================
*> EOF FLAGS
*> ============================================================
01 WS-EOF-MASTER PIC X(01) VALUE 'N'.
88 WS-EOF-MASTER-YES VALUE 'Y' FALSE 'N'.
01 WS-EOF-DETAIL PIC X(01) VALUE 'N'.
88 WS-EOF-DETAIL-YES VALUE 'Y' FALSE 'N'.
*> ============================================================
*> KEY FIELDS
*> ============================================================
01 WS-MASTER-KEY PIC X(10).
01 WS-DETAIL-KEY PIC X(10).
01 WS-GROUP-KEY PIC X(10).
*> ============================================================
*> CONTROL TOTALS
*> ============================================================
01 WS-CONTROL-TOTALS.
05 WS-MASTER-COUNT PIC 9(09) VALUE 0.
05 WS-DETAIL-COUNT PIC 9(09) VALUE 0.
05 WS-MATCH-COUNT PIC 9(09) VALUE 0.
05 WS-UNMATCH-MASTER PIC 9(09) VALUE 0.
05 WS-UNMATCH-DETAIL PIC 9(09) VALUE 0.
05 WS-TOTAL-MASTER-IN PIC 9(09) VALUE 0.
05 WS-TOTAL-DETAIL-IN PIC 9(09) VALUE 0.
*> ============================================================
*> HASH TOTALS — verify financial data integrity
*> ============================================================
01 WS-HASH-TOTALS.
05 WS-INPUT-HASH-AMT PIC 9(15) VALUE 0.
05 WS-OUTPUT-HASH-AMT PIC 9(15) VALUE 0.
05 WS-DETAIL-HASH-AMT PIC 9(15) VALUE 0.
05 WS-TOTAL-HASH-OUTPUT PIC 9(15) VALUE 0.
*> ============================================================
*> CONTRACT ELIGIBILITY FIELDS
*> ============================================================
01 WS-CONTRACT-INFO.
05 WS-CONTRACT-STATUS PIC X(01).
88 WS-CONTRACT-ACTIVE VALUE 'A'.
88 WS-CONTRACT-SUSPENDED VALUE 'S'.
88 WS-CONTRACT-TERMINATED VALUE 'T'.
88 WS-CONTRACT-PENDING VALUE 'P'.
05 WS-CONTRACT-EFF-DATE PIC 9(08).
05 WS-CONTRACT-EXP-DATE PIC 9(08).
05 WS-CONTRACT-TIER PIC 9(01).
88 WS-TIER-BASIC VALUE 1.
88 WS-TIER-PREMIUM VALUE 2.
88 WS-TIER-ENTERPRISE VALUE 3.
*> ============================================================
*> PLAN ELIGIBILITY FIELDS
*> ============================================================
01 WS-PLAN-INFO.
05 WS-PLAN-CODE PIC X(03).
05 WS-PLAN-EFF-DATE PIC 9(08).
05 WS-PLAN-EXP-DATE PIC 9(08).
05 WS-PLAN-STATUS PIC X(01).
88 WS-PLAN-ACTIVE VALUE 'A'.
88 WS-PLAN-DISCONTINUED VALUE 'D'.
88 WS-PLAN-PENDING VALUE 'P'.
05 WS-PLAN-CATEGORY PIC X(02).
88 WS-PLAN-VOICE VALUE 'VO'.
88 WS-PLAN-DATA VALUE 'DA'.
88 WS-PLAN-MESSAGING VALUE 'MS'.
88 WS-PLAN-COMBO VALUE 'CO'.
*> ============================================================
*> DUPLICATE PLAN DETECTION
*> ============================================================
01 WS-DUP-TABLE.
05 WS-DUP-ENTRY OCCURS 20 TIMES.
10 WS-DUP-CONTRACT-ID PIC X(10).
10 WS-DUP-PLAN-CODE PIC X(03).
10 WS-DUP-COUNT PIC 9(02).
01 WS-DUP-INDEX PIC 9(02) VALUE 0.
01 WS-DUP-FOUND PIC X(01) VALUE 'N'.
88 WS-DUP-FOUND-YES VALUE 'Y' FALSE 'N'.
*> ============================================================
*> AUDIT / LOGGING FIELDS
*> ============================================================
01 WS-CURRENT-TIME.
05 WS-CURRENT-HOUR PIC 9(02).
05 WS-CURRENT-MINUTE PIC 9(02).
05 WS-CURRENT-SECOND PIC 9(02).
05 WS-CURRENT-HUND PIC 9(02).
01 WS-TIMESTAMP PIC X(20).
01 WS-PROGRAM-NAME PIC X(20) VALUE '18-matching-MN-to-M'.
*> ============================================================
*> ERROR LOG FIELDS
*> ============================================================
01 WS-ERROR-COUNT PIC 9(03) VALUE 0.
01 WS-ERROR-MESSAGE PIC X(80).
01 WS-ERROR-DETAIL.
05 FILLER PIC X(10) VALUE 'ERROR #'.
05 ED-NUM PIC Z(9).
05 FILLER PIC X(02) VALUE ': '.
05 ED-MESSAGE PIC X(80).
05 FILLER PIC X(05) VALUE ' KEY='.
05 ED-KEY PIC X(10).
*> ============================================================
*> REPORT LINES
*> ============================================================
01 WS-AUDIT-HEADER.
05 FILLER PIC X(40) VALUE
'=== 18-matching-MN-to-M AUDIT REPORT ==='.
01 WS-AUDIT-FOOTER.
05 FILLER PIC X(50) VALUE
'--- END OF 18-matching-MN-to-M AUDIT REPORT ---'.
01 WS-AUDIT-LINE1.
05 FILLER PIC X(21) VALUE 'Total Master Records:'.
05 AL-MASTER-IN PIC Z(9)9.
05 FILLER PIC X(10) VALUE ' Matched:'.
05 AL-MATCHED PIC Z(9)9.
05 FILLER PIC X(10) VALUE ' Unmatch:'.
05 AL-UNMATCH-M PIC Z(9)9.
01 WS-AUDIT-LINE2.
05 FILLER PIC X(21) VALUE 'Total Detail Records:'.
05 AL-DETAIL-IN PIC Z(9)9.
05 FILLER PIC X(10) VALUE ' Unmatch:'.
05 AL-UNMATCH-D PIC Z(9)9.
01 WS-AUDIT-LINE3.
05 FILLER PIC X(21) VALUE 'Input Hash Amount: '.
05 AL-IN-HASH PIC Z(14)9.
01 WS-AUDIT-LINE4.
05 FILLER PIC X(21) VALUE 'Output Hash Amount: '.
05 AL-OUT-HASH PIC Z(14)9.
01 WS-AUDIT-LINE5.
05 FILLER PIC X(21) VALUE 'Detail Hash Amount: '.
05 AL-DTL-HASH PIC Z(14)9.
01 WS-AUDIT-LINE6.
05 FILLER PIC X(21) VALUE 'Hash Comparison: '.
05 AL-HASH-RESULT PIC X(10).
01 WS-AUDIT-LINE7.
05 FILLER PIC X(21) VALUE 'Control Check: '.
05 AL-CTRL-RESULT PIC X(10).
01 WS-AUDIT-TRACE.
05 FILLER PIC X(10) VALUE '[TRACE] '.
05 AT-TIMESTAMP PIC X(08).
05 FILLER PIC X(02) VALUE ' '.
05 AT-MESSAGE PIC X(80).
*> ============================================================
*> WORKING VARIABLES
*> ============================================================
01 WS-I PIC 9(02).
01 WS-J PIC 9(02).
01 WS-AMOUNT-IN PIC 9(09).
01 WS-AMOUNT-OUT PIC 9(09).
01 WS-AMOUNT-DTL PIC 9(09).
01 WS-CONTROL-OK PIC X(01) VALUE 'Y'.
01 WS-HASH-OK PIC X(01) VALUE 'Y'.
01 WS-TELECOM-BILLING.
COPY "telecom/TEL-BILLING.cpy".
*>
PROCEDURE DIVISION.
*> ============================================================
*> 1000-INIT — Initialization Section
*> ============================================================
1000-INIT SECTION.
1000-START.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 1000-INIT start'.
PERFORM 7000-TRACE THRU 7000-TRACE-EXIT
VARYING WS-I FROM 1 BY 1
UNTIL WS-I > 1.
MOVE 0 TO WS-MASTER-COUNT
MOVE 0 TO WS-DETAIL-COUNT
MOVE 0 TO WS-MATCH-COUNT
MOVE 0 TO WS-UNMATCH-MASTER
MOVE 0 TO WS-UNMATCH-DETAIL
MOVE 0 TO WS-TOTAL-MASTER-IN
MOVE 0 TO WS-TOTAL-DETAIL-IN
MOVE 0 TO WS-INPUT-HASH-AMT
MOVE 0 TO WS-OUTPUT-HASH-AMT
MOVE 0 TO WS-DETAIL-HASH-AMT
MOVE 0 TO WS-ERROR-COUNT
MOVE 0 TO WS-DUP-INDEX
MOVE 'Y' TO WS-CONTROL-OK
MOVE 'Y' TO WS-HASH-OK.
ACCEPT WS-CURRENT-TIME FROM TIME.
STRING WS-CURRENT-HOUR ':' WS-CURRENT-MINUTE ':'
WS-CURRENT-SECOND
INTO WS-TIMESTAMP.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 1000-INIT complete '
WS-TIMESTAMP.
1000-EXIT.
EXIT.
*> ============================================================
*> 2000-OPEN — File Open Section
*> ============================================================
2000-OPEN SECTION.
2000-START.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 2000-OPEN start'.
OPEN INPUT FILE-MASTER FILE-DETAIL.
IF WS-FILE-STATUS-1 NOT = '00'
MOVE 'ERROR: Cannot open FILE-MASTER, status='
TO WS-ERROR-MESSAGE
STRING WS-ERROR-MESSAGE WS-FILE-STATUS-1
INTO WS-ERROR-MESSAGE
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
IF WS-FILE-STATUS-2 NOT = '00'
MOVE 'ERROR: Cannot open FILE-DETAIL, status='
TO WS-ERROR-MESSAGE
STRING WS-ERROR-MESSAGE WS-FILE-STATUS-2
INTO WS-ERROR-MESSAGE
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
OPEN OUTPUT FILE-OUT.
IF WS-FILE-STATUS-3 NOT = '00'
MOVE 'ERROR: Cannot open FILE-OUT, status='
TO WS-ERROR-MESSAGE
STRING WS-ERROR-MESSAGE WS-FILE-STATUS-3
INTO WS-ERROR-MESSAGE
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
OPEN OUTPUT AUDIT-FILE.
IF WS-FILE-STATUS-4 NOT = '00'
DISPLAY 'WARNING: Cannot open AUDIT-FILE, status='
WS-FILE-STATUS-4
END-IF.
OPEN OUTPUT ERROR-FILE.
IF WS-FILE-STATUS-5 NOT = '00'
DISPLAY 'WARNING: Cannot open ERROR-FILE, status='
WS-FILE-STATUS-5
END-IF.
*> Write audit header
WRITE AUDIT-LINE FROM WS-AUDIT-HEADER.
IF WS-FILE-STATUS-4 NOT = '00'
DISPLAY 'WARNING: AUDIT write error, status='
WS-FILE-STATUS-4
END-IF.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 2000-OPEN complete'
' — all files opened OK'.
2000-EXIT.
EXIT.
*> ============================================================
*> 3000-PROCESS — Main Processing Section
*> ============================================================
3000-PROCESS SECTION.
3000-START.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 3000-PROCESS start'.
*> --- Initial reads ---
PERFORM 3100-READ-MASTER THRU 3100-READ-MASTER-EXIT.
PERFORM 3200-READ-DETAIL THRU 3200-READ-DETAIL-EXIT.
*> --- Main merge loop ---
PERFORM UNTIL WS-EOF-MASTER-YES OR WS-EOF-DETAIL-YES
MOVE STD-KEY OF MASTER-REC TO WS-MASTER-KEY
MOVE STD-KEY OF DETAIL-REC TO WS-DETAIL-KEY
DISPLAY '[TRACE] MasterKey=' WS-MASTER-KEY
' DetailKey=' WS-DETAIL-KEY
IF WS-MASTER-KEY = WS-DETAIL-KEY
*> Match found: validate contract then write output
PERFORM 3300-VALIDATE-CONTRACT
THRU 3300-VALIDATE-CONTRACT-EXIT
IF WS-CONTRACT-ACTIVE
PERFORM 3400-VALIDATE-PLAN
THRU 3400-VALIDATE-PLAN-EXIT
PERFORM 3500-CHECK-DUPLICATE
THRU 3500-CHECK-DUPLICATE-EXIT
IF NOT WS-DUP-FOUND-YES
ADD 1 TO WS-MATCH-COUNT
MOVE STD-DATA-2 OF MASTER-REC
TO WS-AMOUNT-OUT
ADD WS-AMOUNT-OUT TO WS-OUTPUT-HASH-AMT
MOVE MASTER-REC TO OUT-REC
WRITE OUT-REC
IF WS-FILE-STATUS-3 NOT = '00'
MOVE 'ERROR writing FILE-OUT, status='
TO WS-ERROR-MESSAGE
STRING WS-ERROR-MESSAGE
WS-FILE-STATUS-3
INTO WS-ERROR-MESSAGE
MOVE WS-MASTER-KEY TO ED-KEY
PERFORM 6000-ERROR
THRU 6000-ERROR-EXIT
END-IF
DISPLAY '[TRACE] MATCH: key='
WS-MASTER-KEY ' written to output'
ELSE
DISPLAY '[TRACE] SKIP: key=' WS-MASTER-KEY
' — duplicate plan'
ADD 1 TO WS-UNMATCH-MASTER
END-IF
ELSE
DISPLAY '[TRACE] SKIP: key=' WS-MASTER-KEY
' — inactive contract'
ADD 1 TO WS-UNMATCH-MASTER
END-IF
*> Advance master to next record (M side of M:N)
PERFORM 3100-READ-MASTER THRU 3100-READ-MASTER-EXIT
ELSE IF WS-MASTER-KEY < WS-DETAIL-KEY
*> Master key not in detail: skip entire master group
DISPLAY '[TRACE] UNMATCHED master key='
WS-MASTER-KEY
MOVE WS-MASTER-KEY TO WS-GROUP-KEY
PERFORM UNTIL WS-EOF-MASTER-YES
PERFORM 3100-READ-MASTER
THRU 3100-READ-MASTER-EXIT
IF WS-EOF-MASTER-YES
EXIT PERFORM
END-IF
MOVE STD-KEY OF MASTER-REC TO WS-MASTER-KEY
IF WS-MASTER-KEY NOT = WS-GROUP-KEY
EXIT PERFORM
END-IF
END-PERFORM
ELSE
*> Detail key < master key: skip detail group
DISPLAY '[TRACE] UNMATCHED detail key='
WS-DETAIL-KEY
MOVE WS-DETAIL-KEY TO WS-GROUP-KEY
PERFORM UNTIL WS-EOF-DETAIL-YES
PERFORM 3200-READ-DETAIL
THRU 3200-READ-DETAIL-EXIT
IF WS-EOF-DETAIL-YES
EXIT PERFORM
END-IF
MOVE STD-KEY OF DETAIL-REC TO WS-DETAIL-KEY
IF WS-DETAIL-KEY NOT = WS-GROUP-KEY
EXIT PERFORM
END-IF
END-PERFORM
END-IF
END-PERFORM.
*> --- Process any remaining records ---
IF NOT WS-EOF-MASTER-YES
ADD 1 TO WS-UNMATCH-MASTER
PERFORM UNTIL WS-EOF-MASTER-YES
PERFORM 3100-READ-MASTER
THRU 3100-READ-MASTER-EXIT
END-PERFORM
END-IF.
IF NOT WS-EOF-DETAIL-YES
ADD 1 TO WS-UNMATCH-DETAIL
PERFORM UNTIL WS-EOF-DETAIL-YES
PERFORM 3200-READ-DETAIL
THRU 3200-READ-DETAIL-EXIT
END-PERFORM
END-IF.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME
' 3000-PROCESS complete — matched='
WS-MATCH-COUNT.
3000-PROCESS-EXIT.
EXIT.
*> ============================================================
*> 3100-READ-MASTER — Read next master record
*> ============================================================
3100-READ-MASTER SECTION.
3100-READ-MASTER-START.
READ FILE-MASTER
AT END SET WS-EOF-MASTER-YES TO TRUE
END-READ.
IF WS-FILE-STATUS-1 NOT = '00' AND NOT = '10'
MOVE 'ERROR reading FILE-MASTER, status='
TO WS-ERROR-MESSAGE
STRING WS-ERROR-MESSAGE WS-FILE-STATUS-1
INTO WS-ERROR-MESSAGE
MOVE WS-MASTER-KEY TO ED-KEY
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
END-IF.
IF NOT WS-EOF-MASTER-YES
ADD 1 TO WS-MASTER-COUNT
ADD 1 TO WS-TOTAL-MASTER-IN
MOVE STD-DATA-2 OF MASTER-REC TO WS-AMOUNT-IN
ADD WS-AMOUNT-IN TO WS-INPUT-HASH-AMT
END-IF.
3100-READ-MASTER-EXIT.
EXIT.
*> ============================================================
*> 3200-READ-DETAIL — Read next detail record
*> ============================================================
3200-READ-DETAIL SECTION.
3200-READ-DETAIL-START.
READ FILE-DETAIL
AT END SET WS-EOF-DETAIL-YES TO TRUE
END-READ.
IF WS-FILE-STATUS-2 NOT = '00' AND NOT = '10'
MOVE 'ERROR reading FILE-DETAIL, status='
TO WS-ERROR-MESSAGE
STRING WS-ERROR-MESSAGE WS-FILE-STATUS-2
INTO WS-ERROR-MESSAGE
MOVE WS-DETAIL-KEY TO ED-KEY
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
END-IF.
IF NOT WS-EOF-DETAIL-YES
ADD 1 TO WS-DETAIL-COUNT
ADD 1 TO WS-TOTAL-DETAIL-IN
MOVE STD-DATA-2 OF DETAIL-REC TO WS-AMOUNT-DTL
ADD WS-AMOUNT-DTL TO WS-DETAIL-HASH-AMT
END-IF.
3200-READ-DETAIL-EXIT.
EXIT.
*> ============================================================
*> 3300-VALIDATE-CONTRACT — Validate contract eligibility
*> ============================================================
3300-VALIDATE-CONTRACT SECTION.
3300-VALIDATE-CONTRACT-START.
MOVE STD-DATA-1 OF MASTER-REC TO WS-CONTRACT-INFO.
*> STD-DATA-1 contains: status(1) + eff-date(8) + exp-date(8)
*> + tier(1) + reserved(2) = 20 bytes
MOVE STD-DATA-1 OF MASTER-REC(1:1) TO WS-CONTRACT-STATUS.
MOVE STD-DATA-1 OF MASTER-REC(2:8) TO WS-CONTRACT-EFF-DATE.
MOVE STD-DATA-1 OF MASTER-REC(10:8) TO WS-CONTRACT-EXP-DATE.
MOVE STD-DATA-1 OF MASTER-REC(18:1) TO WS-CONTRACT-TIER.
DISPLAY '[TRACE] Contract status=' WS-CONTRACT-STATUS
' tier=' WS-CONTRACT-TIER.
EVALUATE TRUE
WHEN WS-CONTRACT-ACTIVE
DISPLAY '[TRACE] Contract ACTIVE — eligible'
WHEN WS-CONTRACT-SUSPENDED
DISPLAY '[TRACE] Contract SUSPENDED — ineligible'
ADD 1 TO WS-ERROR-COUNT
WHEN WS-CONTRACT-TERMINATED
DISPLAY '[TRACE] Contract TERMINATED — ineligible'
ADD 1 TO WS-ERROR-COUNT
WHEN WS-CONTRACT-PENDING
DISPLAY '[TRACE] Contract PENDING — ineligible'
ADD 1 TO WS-ERROR-COUNT
WHEN OTHER
DISPLAY '[TRACE] Unknown contract status - rejected'
ADD 1 TO WS-ERROR-COUNT
END-EVALUATE.
3300-VALIDATE-CONTRACT-EXIT.
EXIT.
*> ============================================================
*> 3400-VALIDATE-PLAN — Validate plan eligibility
*> ============================================================
3400-VALIDATE-PLAN SECTION.
3400-VALIDATE-PLAN-START.
MOVE STD-DATA-1 OF DETAIL-REC(1:3) TO WS-PLAN-CODE.
MOVE STD-DATA-1 OF DETAIL-REC(4:8) TO WS-PLAN-EFF-DATE.
MOVE STD-DATA-1 OF DETAIL-REC(12:8) TO WS-PLAN-EXP-DATE.
MOVE STD-DATA-1 OF DETAIL-REC(20:1) TO WS-PLAN-STATUS.
MOVE STD-DATA-1 OF DETAIL-REC(1:2) TO WS-PLAN-CATEGORY.
DISPLAY '[TRACE] Plan code=' WS-PLAN-CODE
' category=' WS-PLAN-CATEGORY
' status=' WS-PLAN-STATUS.
IF NOT WS-PLAN-ACTIVE
DISPLAY '[TRACE] Plan NOT ACTIVE — skipping'
ADD 1 TO WS-ERROR-COUNT
ELSE
DISPLAY '[TRACE] Plan ACTIVE — eligible'
END-IF.
3400-VALIDATE-PLAN-EXIT.
EXIT.
*> ============================================================
*> 3500-CHECK-DUPLICATE — Detect duplicate plan assignments
*> ============================================================
3500-CHECK-DUPLICATE SECTION.
3500-CHECK-DUPLICATE-START.
MOVE 'N' TO WS-DUP-FOUND.
IF WS-DUP-INDEX > 0
PERFORM VARYING WS-I FROM 1 BY 1
UNTIL WS-I > WS-DUP-INDEX
IF WS-DUP-CONTRACT-ID(WS-I) = WS-MASTER-KEY
AND WS-DUP-PLAN-CODE(WS-I) = WS-PLAN-CODE
SET WS-DUP-FOUND-YES TO TRUE
DISPLAY '[TRACE] DUPLICATE: contract='
WS-MASTER-KEY
' plan=' WS-PLAN-CODE
EXIT PERFORM
END-IF
END-PERFORM
END-IF.
IF NOT WS-DUP-FOUND-YES
ADD 1 TO WS-DUP-INDEX
MOVE WS-MASTER-KEY
TO WS-DUP-CONTRACT-ID(WS-DUP-INDEX)
MOVE WS-PLAN-CODE
TO WS-DUP-PLAN-CODE(WS-DUP-INDEX)
MOVE 1 TO WS-DUP-COUNT(WS-DUP-INDEX)
DISPLAY '[TRACE] NEW assignment: contract='
WS-MASTER-KEY ' plan=' WS-PLAN-CODE
END-IF.
3500-CHECK-DUPLICATE-EXIT.
EXIT.
*> ============================================================
*> 4000-VALIDATE — Control total and hash validation
*> ============================================================
4000-VALIDATE SECTION.
4000-START.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 4000-VALIDATE start'.
*> --- Control total check: total-in = matched + unmatched ---
COMPUTE WS-TOTAL-MASTER-IN =
WS-MATCH-COUNT + WS-UNMATCH-MASTER.
IF WS-TOTAL-MASTER-IN NOT =
WS-MASTER-COUNT
MOVE 'N' TO WS-CONTROL-OK
DISPLAY 'CONTROL FAIL: master-in=' WS-MASTER-COUNT
' matched+unmatched='
WS-TOTAL-MASTER-IN
STRING 'CONTROL FAIL: master-in='
WS-MASTER-COUNT
' matched+unmatched='
WS-TOTAL-MASTER-IN
INTO WS-ERROR-MESSAGE
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
ELSE
DISPLAY 'CONTROL OK: master-in=' WS-MASTER-COUNT
' = matched+unmatched'
END-IF.
*> --- Hash total verification ---
COMPUTE WS-TOTAL-HASH-OUTPUT =
WS-OUTPUT-HASH-AMT + WS-DETAIL-HASH-AMT
IF WS-INPUT-HASH-AMT NOT =
WS-TOTAL-HASH-OUTPUT
MOVE 'N' TO WS-HASH-OK
DISPLAY 'HASH FAIL: input=' WS-INPUT-HASH-AMT
' output+detail='
WS-TOTAL-HASH-OUTPUT
STRING 'HASH FAIL: input='
WS-INPUT-HASH-AMT
' output+detail='
WS-TOTAL-HASH-OUTPUT
INTO WS-ERROR-MESSAGE
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
ELSE
DISPLAY 'HASH OK: input=' WS-INPUT-HASH-AMT
' = output+detail'
END-IF.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME
' 4000-VALIDATE complete'.
4000-VALIDATE-EXIT.
EXIT.
*> ============================================================
*> 5000-REPORT — Generate audit report
*> ============================================================
5000-REPORT SECTION.
5000-START.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 5000-REPORT start'.
MOVE WS-MASTER-COUNT TO AL-MASTER-IN.
MOVE WS-MATCH-COUNT TO AL-MATCHED.
MOVE WS-UNMATCH-MASTER TO AL-UNMATCH-M.
WRITE AUDIT-LINE FROM WS-AUDIT-LINE1.
IF WS-FILE-STATUS-4 NOT = '00'
DISPLAY 'WARNING: AUDIT write error, status='
WS-FILE-STATUS-4
END-IF.
MOVE WS-DETAIL-COUNT TO AL-DETAIL-IN.
MOVE WS-UNMATCH-DETAIL TO AL-UNMATCH-D.
WRITE AUDIT-LINE FROM WS-AUDIT-LINE2.
IF WS-FILE-STATUS-4 NOT = '00'
DISPLAY 'WARNING: AUDIT write error, status='
WS-FILE-STATUS-4
END-IF.
MOVE WS-INPUT-HASH-AMT TO AL-IN-HASH.
WRITE AUDIT-LINE FROM WS-AUDIT-LINE3.
MOVE WS-OUTPUT-HASH-AMT TO AL-OUT-HASH.
WRITE AUDIT-LINE FROM WS-AUDIT-LINE4.
MOVE WS-DETAIL-HASH-AMT TO AL-DTL-HASH.
WRITE AUDIT-LINE FROM WS-AUDIT-LINE5.
IF WS-HASH-OK = 'Y'
MOVE 'PASS' TO AL-HASH-RESULT
ELSE
MOVE 'FAIL' TO AL-HASH-RESULT
END-IF.
WRITE AUDIT-LINE FROM WS-AUDIT-LINE6.
IF WS-CONTROL-OK = 'Y'
MOVE 'PASS' TO AL-CTRL-RESULT
ELSE
MOVE 'FAIL' TO AL-CTRL-RESULT
END-IF.
WRITE AUDIT-LINE FROM WS-AUDIT-LINE7.
DISPLAY '18-matching-MN-to-M: Master-in='
WS-MASTER-COUNT
' Detail-in=' WS-DETAIL-COUNT
' Matched=' WS-MATCH-COUNT
' Unmatch-M=' WS-UNMATCH-MASTER
' Unmatch-D=' WS-UNMATCH-DETAIL.
WRITE AUDIT-LINE FROM WS-AUDIT-FOOTER.
5000-REPORT-EXIT.
EXIT.
*> ============================================================
*> 6000-ERROR — Error handling
*> ============================================================
6000-ERROR SECTION.
6000-ERROR-START.
ADD 1 TO WS-ERROR-COUNT.
MOVE WS-ERROR-COUNT TO ED-NUM.
MOVE WS-ERROR-MESSAGE TO ED-MESSAGE.
DISPLAY WS-ERROR-DETAIL.
WRITE ERROR-LINE FROM WS-ERROR-DETAIL.
IF WS-FILE-STATUS-5 NOT = '00'
DISPLAY 'WARNING: Cannot write to ERROR-FILE, status='
WS-FILE-STATUS-5
END-IF.
6000-ERROR-EXIT.
EXIT.
*> ============================================================
*> 7000-AUDIT — Tracing / audit log
*> ============================================================
7000-AUDIT SECTION.
7000-AUDIT-START.
ACCEPT WS-CURRENT-TIME FROM TIME.
STRING WS-CURRENT-HOUR ':' WS-CURRENT-MINUTE ':'
WS-CURRENT-SECOND
INTO AT-TIMESTAMP.
MOVE '7000-AUDIT entry' TO AT-MESSAGE.
WRITE AUDIT-LINE FROM WS-AUDIT-TRACE.
7000-AUDIT-EXIT.
EXIT.
*> ============================================================
*> 7000-TRACE — Write trace line to audit
*> ============================================================
7000-TRACE SECTION.
7000-TRACE-START.
ACCEPT WS-CURRENT-TIME FROM TIME.
STRING WS-CURRENT-HOUR ':' WS-CURRENT-MINUTE ':'
WS-CURRENT-SECOND
INTO AT-TIMESTAMP.
STRING '7000-TRACE iteration ' WS-I INTO AT-MESSAGE.
WRITE AUDIT-LINE FROM WS-AUDIT-TRACE.
7000-TRACE-EXIT.
EXIT.
*> ============================================================
*> 9000-EXIT — Cleanup and close
*> ============================================================
9000-EXIT SECTION.
9000-START.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 9000-EXIT start'.
CLOSE FILE-MASTER FILE-DETAIL FILE-OUT.
IF WS-FILE-STATUS-1 NOT = '00'
DISPLAY 'WARNING: FILE-MASTER close status='
WS-FILE-STATUS-1
END-IF.
IF WS-FILE-STATUS-2 NOT = '00'
DISPLAY 'WARNING: FILE-DETAIL close status='
WS-FILE-STATUS-2
END-IF.
IF WS-FILE-STATUS-3 NOT = '00'
DISPLAY 'WARNING: FILE-OUT close status='
WS-FILE-STATUS-3
END-IF.
CLOSE AUDIT-FILE.
IF WS-FILE-STATUS-4 NOT = '00'
DISPLAY 'WARNING: AUDIT-FILE close status='
WS-FILE-STATUS-4
END-IF.
CLOSE ERROR-FILE.
IF WS-FILE-STATUS-5 NOT = '00'
DISPLAY 'WARNING: ERROR-FILE close status='
WS-FILE-STATUS-5
END-IF.
DISPLAY '18-matching-MN-to-M: WS-MATCH-COUNT='
WS-MATCH-COUNT ' records written'.
DISPLAY '18-matching-MN-to-M: PASS'.
IF WS-ERROR-COUNT > 0
DISPLAY '18-matching-MN-to-M: Errors=' WS-ERROR-COUNT
' — see error-report-18.txt'
END-IF.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 9000-EXIT complete'.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' END'.
STOP RUN.
9000-EXIT-EXIT.
EXIT.
END PROGRAM MatchingMNtoM.