feat: add benchmark-programs — 58 telecom COBOL test programs

作为子目录纳入系统,与核心测试管道协同

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
NB-076
2026-06-25 09:53:21 +08:00
parent 50f9f0f52f
commit 94400d50d4
278 changed files with 44125 additions and 0 deletions
@@ -0,0 +1,49 @@
# 18-matching-MN-to-M: M:N Matching with M Output Records
## 电信业务场景
合同↔套餐M:N→M条。多个合同关联多个套餐的组合匹配,输出按合同件数(M件)的匹配结果。
## Description
M:N matching where both sides can have duplicate keys. The output contains
one record per master record that has at least one matching detail record
with the same key. This tests the ability to handle M:N relationships
and produce one output per matched master.
The algorithm skips master groups with no matching detail key, and skips
detail key groups that have no corresponding master key.
## Record Layout
Standard record (45 bytes, copybook STD-REC.cpy):
| Field | Type | Length | Description |
|------------|-------------------|--------|--------------------|
| STD-KEY | PIC X | 10 | Record key |
| STD-DATA-1 | PIC X | 20 | Text data |
| STD-DATA-2 | PIC 9 | 10 | Numeric data |
| STD-DATA-3 | PIC S9(7)V99 COMP-3| 5 | Packed decimal |
## Files
| File | Purpose |
|---------------------------------|--------------------------------------|
| main-18-matching-MN-to-M.cbl | Main COBOL program (fixed format) |
| data-gen.sh | Generate test data for all files |
| run.sh | Compile, run, verify |
| README.md | This file |
## Data Design
- master.dat: 5 records -- KEY00001 (2), KEY00002 (2), KEY00003 (1)
- detail.dat: 4 records -- KEY00001 (2), KEY00003 (2)
- Matching keys: KEY00001, KEY00003
- Unmatched: KEY00002 (master only)
- Output: 3 records = 135 bytes
## Expected Behavior
- KEY00001 group (2 master records): matches detail KEY00001 -> output 2 records
- KEY00002 group (2 master records): no matching detail -> output 0 records
- KEY00003 group (1 master record): matches detail KEY00003 -> output 1 record
- Total: 3 output records
@@ -0,0 +1,11 @@
=== 18-matching-MN-to-M AUDIT REPORT ===
Total Master Records: 2 Matched: 0 Unmatch: 2
Total Detail Records: 2 Unmatch: 1
Input Hash Amount: 0
Output Hash Amount: 0
Detail Hash Amount: 0
Hash Comparison: PASS
Control Check: PASS
--- END OF 18-matching-MN-to-M AUDIT REPORT ---
[TRACE] 16:35:18 7000-AUDIT entry
[TRACE] 16:35:18 7000-TRACE iteration 02
@@ -0,0 +1 @@
0000000000000000000
@@ -0,0 +1,3 @@
ERROR # 3: ERROR reading FILE-MASTER, status= KEY=0000
ERROR # 4: ERROR reading FILE-DETAIL, status= KEY=0000
ERROR # 7: ERROR reading FILE-DETAIL, status= KEY=0000
@@ -0,0 +1,832 @@
*> ============================================================
*> 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.
@@ -0,0 +1 @@
0000000000000000000