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,27 @@
# 25-subprogram
## 电信业务场景
计费子程序。通过CALL语句调用计费计算子程序,传入用量和单价参数,返回计费金额。演示USING参数传递和RETURN-CODE使用。
## Purpose
Tests COBOL subprogram calling conventions including CALL with literal names,
CALL with variable program names, and CALL with IS INITIAL.
## Architecture
- **callee.cbl** — Subprogram (PROGRAM-ID. callee) that adds two numbers via
LINKAGE SECTION parameters and tracks first-call state.
- **caller.cbl** — Main program (PROGRAM-ID. caller) with three test cases.
## Tests
1. **CALL literal**: CALL "callee" USING 100 200 -> expects 300
2. **CALL variable**: CALL WS-PGM-NAME USING 10 20 -> expects 30
3. **CALL IS INITIAL**: CALL "callee" IS INITIAL USING 1 2 -> expects 3,
forces subprogram reinitialization
## Key Techniques
- CALL USING for parameter passing
- LINKAGE SECTION for parameter definitions
- GOBACK to return to caller
- RETURN-CODE for status communication
- IS INITIAL to reinitialize subprogram state
@@ -0,0 +1,31 @@
*> ============================================================
*> callee : 计费子程序被调用侧 (Billing Subprogram Callee)
*> Input : LS-NUM1, LS-NUM2 (LINKAGE参数)
*> Output: LS-RESULT (计算结果返却)
*> Coverage: C-N001~N008, C-A001, C-R001, C-R002
*> ============================================================
IDENTIFICATION DIVISION.
PROGRAM-ID. callee.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-INITIALIZED PIC X VALUE 'N'.
88 WS-INITIALIZED-YES VALUE 'Y'.
LINKAGE SECTION.
01 LS-NUM1 PIC 9(10).
01 LS-NUM2 PIC 9(10).
01 LS-RESULT PIC 9(10).
PROCEDURE DIVISION USING LS-NUM1 LS-NUM2 LS-RESULT.
IF NOT WS-INITIALIZED-YES
DISPLAY "callee: FIRST CALL"
SET WS-INITIALIZED-YES TO TRUE
END-IF
COMPUTE LS-RESULT = LS-NUM1 + LS-NUM2
MOVE 0 TO RETURN-CODE
GOBACK
.
END PROGRAM callee.
@@ -0,0 +1,125 @@
*> ============================================================
*> caller-nested : 计费子程序嵌套调用 (Nested Subprogram Call)
*> Input : WS-VAL-A, WS-VAL-B (传递参数)
*> Output: WS-RESULT (嵌套计算结果)
*> Coverage: C-N009
*> ============================================================
IDENTIFICATION DIVISION.
PROGRAM-ID. CallerNested.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-LEVEL PIC 9(1).
01 WS-VAL-A PIC 9(5).
01 WS-VAL-B PIC 9(5).
01 WS-RESULT PIC 9(10).
01 WS-PASS PIC 9(2) VALUE 0.
01 WS-FAIL PIC 9(2) VALUE 0.
PROCEDURE DIVISION.
MAIN.
DISPLAY "CALLER-NESTED: 3-level CALL test (C-N009)"
*> Test: CALLER → SUB-A → SUB-B → SUB-C RETURNING
MOVE 1 TO WS-LEVEL.
MOVE 10 TO WS-VAL-A.
MOVE 20 TO WS-VAL-B.
CALL 'SUB-A' USING WS-LEVEL WS-VAL-A WS-VAL-B WS-RESULT.
IF WS-RESULT = 100 *> (10+20) + (10+20) + 20 = 100
ADD 1 TO WS-PASS
DISPLAY "C-N009: PASS - 3-level result=" WS-RESULT
ELSE
ADD 1 TO WS-FAIL
DISPLAY "C-N009: FAIL - result=" WS-RESULT
END-IF.
*> Test: CALL with RETURN-CODE from nested
MOVE 2 TO WS-LEVEL.
MOVE 5 TO WS-VAL-A.
MOVE 7 TO WS-VAL-B.
CALL 'SUB-A' USING WS-LEVEL WS-VAL-A WS-VAL-B WS-RESULT.
IF WS-RESULT = 31 *> (5+7) + (5+7) + 7 = 31
ADD 1 TO WS-PASS
DISPLAY "C-N009-2: PASS - nested result=" WS-RESULT
ELSE
ADD 1 TO WS-FAIL
DISPLAY "C-N009-2: FAIL - result=" WS-RESULT
END-IF.
*> CALL nonexistent program (TC-A051)
DISPLAY "TC-A051: CALL non-existent program"
CALL 'NOPGM00' USING WS-VAL-A
ON EXCEPTION
ADD 1 TO WS-PASS
DISPLAY "TC-A051: PASS - exception raised"
NOT ON EXCEPTION
ADD 1 TO WS-FAIL
DISPLAY "TC-A051: FAIL - no exception"
END-CALL.
DISPLAY " "
DISPLAY "CALLER-NESTED: PASS=" WS-PASS " FAIL=" WS-FAIL
IF WS-FAIL = 0
DISPLAY "CALLER-NESTED: ALL PASSED"
STOP RUN RETURNING 0
ELSE
DISPLAY "CALLER-NESTED: FAILED"
STOP RUN RETURNING 1
END-IF
.
END PROGRAM CallerNested.
IDENTIFICATION DIVISION.
PROGRAM-ID. SUB-A.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-RET PIC 9(10).
LINKAGE SECTION.
01 LK-LEVEL PIC 9(1).
01 LK-VAL-A PIC 9(5).
01 LK-VAL-B PIC 9(5).
01 LK-RESULT PIC 9(10).
PROCEDURE DIVISION USING LK-LEVEL LK-VAL-A LK-VAL-B LK-RESULT.
ADD LK-VAL-A TO LK-VAL-B GIVING WS-RET.
CALL 'SUB-B' USING LK-LEVEL LK-VAL-A LK-VAL-B WS-RET LK-RESULT.
GOBACK.
END PROGRAM SUB-A.
IDENTIFICATION DIVISION.
PROGRAM-ID. SUB-B.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-TEMP PIC 9(10).
LINKAGE SECTION.
01 LK-LEVEL PIC 9(1).
01 LK-VAL-A PIC 9(5).
01 LK-VAL-B PIC 9(5).
01 LK-INPUT PIC 9(10).
01 LK-RESULT PIC 9(10).
PROCEDURE DIVISION USING LK-LEVEL LK-VAL-A LK-VAL-B
LK-INPUT LK-RESULT.
COMPUTE WS-TEMP = LK-INPUT + LK-VAL-B.
IF LK-LEVEL = 1
COMPUTE LK-RESULT = WS-TEMP
ELSE
CALL 'SUB-C' USING LK-VAL-A LK-VAL-B WS-TEMP LK-RESULT
END-IF.
GOBACK.
END PROGRAM SUB-B.
IDENTIFICATION DIVISION.
PROGRAM-ID. SUB-C.
DATA DIVISION.
LINKAGE SECTION.
01 LK-VAL-A PIC 9(5).
01 LK-VAL-B PIC 9(5).
01 LK-INPUT PIC 9(10).
01 LK-RESULT PIC 9(10).
PROCEDURE DIVISION USING LK-VAL-A LK-VAL-B LK-INPUT LK-RESULT.
COMPUTE LK-RESULT = LK-INPUT + LK-VAL-B.
GOBACK.
END PROGRAM SUB-C.
@@ -0,0 +1,66 @@
*> ============================================================
*> caller : 计费子程序调用者 (Billing Subprogram Caller)
*> Input : WS-N1, WS-N2 (用量参数)
*> Output: WS-RESULT (CALL计算结果)
*> Coverage: C-N001~N008, C-A001, C-R001, C-R002
*> ============================================================
IDENTIFICATION DIVISION.
PROGRAM-ID. caller.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-PGM-NAME PIC X(8) VALUE "callee ".
01 WS-N1 PIC 9(10).
01 WS-N2 PIC 9(10).
01 WS-RESULT1 PIC 9(10).
01 WS-RESULT2 PIC 9(10).
01 WS-RESULT3 PIC 9(10).
01 WS-ALL-PASS PIC X VALUE 'Y'.
88 WS-ALL-PASS-YES VALUE 'Y'.
PROCEDURE DIVISION.
MAIN.
*> Test 1: CALL literal program name
MOVE 100 TO WS-N1
MOVE 200 TO WS-N2
CALL "callee" USING WS-N1 WS-N2 WS-RESULT1
IF WS-RESULT1 = 300
DISPLAY "Test 1: PASS"
ELSE
DISPLAY "Test 1: FAIL (expected 300, got " WS-RESULT1 ")"
MOVE 'N' TO WS-ALL-PASS
END-IF
*> Test 2: CALL variable program name
MOVE 10 TO WS-N1
MOVE 20 TO WS-N2
CALL WS-PGM-NAME USING WS-N1 WS-N2 WS-RESULT2
IF WS-RESULT2 = 30
DISPLAY "Test 2: PASS"
ELSE
DISPLAY "Test 2: FAIL (expected 30, got " WS-RESULT2 ")"
MOVE 'N' TO WS-ALL-PASS
END-IF
*> Test 3: CALL IS INITIAL (forces subprogram reinitialization)
MOVE 1 TO WS-N1
MOVE 2 TO WS-N2
CALL "callee" IS INITIAL USING WS-N1 WS-N2 WS-RESULT3
IF WS-RESULT3 = 3
DISPLAY "Test 3: PASS"
ELSE
DISPLAY "Test 3: FAIL (expected 3, got " WS-RESULT3 ")"
MOVE 'N' TO WS-ALL-PASS
END-IF
IF WS-ALL-PASS-YES
DISPLAY "25-subprogram: PASS"
STOP RUN RETURNING 0
ELSE
DISPLAY "25-subprogram: FAIL"
STOP RUN RETURNING 1
END-IF
.
END PROGRAM caller.
@@ -0,0 +1,1110 @@
*> ============================================================
*> 25-subprogram : 计费子程序 (Billing Subprogram) — CALLER
*> Input : CALL 引数 (WS-INPUT-A, WS-INPUT-B: 用量参数)
*> Output: CALL 返り値 (WS-RESULT, RETURN-CODE: 金额)
*> Coverage: C-N001~N008, C-A001, C-R001, C-R002
*>
*> EXPANDED: Added SECTION structure, multiple billing tiers,
*> parameter validation (range checks), CALL with ON EXCEPTION
*> handler, error code mapping, logging subprogram, audit file,
*> error file, control totals, hash totals, tracing.
*> ============================================================
IDENTIFICATION DIVISION.
PROGRAM-ID. Main25Subprogram.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT REPORT-FILE ASSIGN TO "sub-report.txt"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS FS-REPORT.
SELECT AUDIT-FILE ASSIGN TO "audit-report-25.txt"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS FS-AUDIT.
SELECT ERROR-FILE ASSIGN TO "error-report-25.txt"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS FS-ERROR.
DATA DIVISION.
FILE SECTION.
FD REPORT-FILE.
01 REPORT-LINE PIC X(120).
FD AUDIT-FILE.
01 AUDIT-LINE PIC X(120).
FD ERROR-FILE.
01 ERROR-LINE PIC X(120).
WORKING-STORAGE SECTION.
*> ============================================================
*> FILE STATUS
*> ============================================================
01 FS-REPORT PIC X(02).
01 FS-AUDIT PIC X(02).
01 FS-ERROR PIC X(02).
*> ============================================================
*> CALL PARAMETERS
*> ============================================================
01 WS-CALLEE-PGM PIC X(08) VALUE "SUBPGM ".
01 WS-LOGGER-PGM PIC X(08) VALUE "LOGGER ".
01 WS-INPUT-A PIC 9(06) VALUE 0.
01 WS-INPUT-B PIC 9(05) VALUE 0.
01 WS-RESULT PIC 9(10) VALUE 0.
01 WS-STATUS PIC 9(02) VALUE 0.
01 WS-ERROR-CODE PIC 9(02) VALUE 0.
*> ============================================================
*> MULTIPLE BILLING TIERS
*> ============================================================
01 WS-BILLING-TIER PIC 9(01).
88 WS-TIER-BASIC VALUE 1.
88 WS-TIER-PREMIUM VALUE 2.
88 WS-TIER-ENTERPRISE VALUE 3.
01 WS-TIER-DESC PIC X(15).
01 WS-TIER-MULTIPLIER PIC 9(02)V99 VALUE 1.00.
01 WS-TIER-RESULT PIC 9(10) VALUE 0.
*> ============================================================
*> PARAMETER VALIDATION (RANGE CHECKS)
*> ============================================================
01 WS-VALID-RESULT.
05 WS-VALID-A-OK PIC X(01) VALUE 'Y'.
05 WS-VALID-B-OK PIC X(01) VALUE 'Y'.
05 WS-VALID-TIER-OK PIC X(01) VALUE 'Y'.
01 WS-MIN-VALUE PIC 9(05) VALUE 0.
01 WS-MAX-VALUE PIC 9(05) VALUE 99999.
01 WS-TIER-MIN PIC 9(01) VALUE 1.
01 WS-TIER-MAX PIC 9(01) VALUE 3.
*> ============================================================
*> CALL EXCEPTION HANDLER
*> ============================================================
01 WS-EXCEPTION-COUNT PIC 9(02) VALUE 0.
01 WS-CALL-RESULT PIC X(10).
88 WS-CALL-OK VALUE 'OK'.
88 WS-CALL-EXCEPTION VALUE 'EXCEPTION'.
01 WS-EXCEPTION-DATA.
05 WS-EXCEPTION-PGM PIC X(08).
05 WS-EXCEPTION-STATUS PIC 9(04).
*> ============================================================
*> ERROR CODE MAPPING
*> ============================================================
01 WS-ERR-MAP.
05 WS-ERR-CODE PIC 9(02).
05 WS-ERR-MESSAGE PIC X(30).
01 WS-ERR-CODE-INFO.
05 FILLER PIC X(32) VALUE '00OK '.
05 FILLER PIC X(32) VALUE '01INVALID INPUT A '.
05 FILLER PIC X(32) VALUE '02INVALID INPUT B '.
05 FILLER PIC X(32) VALUE '03INPUT OUT OF RANGE '.
05 FILLER PIC X(32) VALUE '04INVALID BILLING TIER '.
05 FILLER PIC X(32) VALUE '05CALCULATION OVERFLOW '.
05 FILLER PIC X(32) VALUE '06SUBPROGRAM NOT FOUND '.
05 FILLER PIC X(32) VALUE '07SUBPROGRAM ERROR '.
05 FILLER PIC X(32) VALUE '08TIER MULTIPLIER ERROR '.
05 FILLER PIC X(32) VALUE '09INTERNAL ERROR '.
01 WS-ERR-CODE-TABLE REDEFINES WS-ERR-CODE-INFO.
05 WS-ERR-ENTRY OCCURS 10 TIMES.
10 WS-EC-CODE PIC X(02).
10 WS-EC-MESSAGE PIC X(30).
*> ============================================================
*> CONTROL TOTALS
*> ============================================================
01 WS-CONTROL-TOTALS.
05 WS-TEST-COUNT PIC 9(02) VALUE 0.
05 WS-PASS-COUNT PIC 9(02) VALUE 0.
05 WS-FAIL-COUNT PIC 9(02) VALUE 0.
05 WS-TOTAL-INPUT-A PIC 9(09) VALUE 0.
05 WS-TOTAL-INPUT-B PIC 9(09) VALUE 0.
05 WS-TOTAL-RESULT PIC 9(15) VALUE 0.
*> ============================================================
*> HASH TOTALS
*> ============================================================
01 WS-HASH-TOTALS.
05 WS-HASH-INPUT PIC 9(15) VALUE 0.
05 WS-HASH-OUTPUT PIC 9(15) VALUE 0.
05 WS-HASH-EXPECTED PIC 9(15) VALUE 0.
*> ============================================================
*> TEST RESULT FIELDS
*> ============================================================
01 WS-TEST-NUM PIC 9(02) VALUE 0.
01 WS-EXPECTED PIC 9(10) VALUE 0.
01 WS-TEST-RESULT PIC X(10).
88 TEST-PASS VALUE "PASS".
88 TEST-FAIL VALUE "FAIL".
*> ============================================================
*> REPORT LINE TEMPLATES
*> ============================================================
01 WS-VERIFY-LINE.
05 FILLER PIC X(10) VALUE " Test ".
05 VL-NUM PIC Z(9).
05 FILLER PIC X(03) VALUE ": ".
05 VL-DESC PIC X(35).
05 FILLER PIC X(02) VALUE " ".
05 VL-RESULT PIC X(10).
01 WS-DETAIL-LINE.
05 FILLER PIC X(10) VALUE " A=".
05 DL-A PIC Z(9)9.
05 FILLER PIC X(05) VALUE " B=".
05 DL-B PIC Z(9)9.
05 FILLER PIC X(10) VALUE " RESULT=".
05 DL-RESULT PIC Z(9)9.
05 FILLER PIC X(10) VALUE " STATUS=".
05 DL-STATUS PIC Z(9).
05 FILLER PIC X(10) VALUE " TIER=".
05 DL-TIER PIC Z(9).
01 WS-AUDIT-HEADER.
05 FILLER PIC X(40) VALUE
'=== 25-subprogram AUDIT REPORT ==='.
01 WS-AUDIT-FOOTER.
05 FILLER PIC X(50) VALUE
'--- END OF 25-subprogram AUDIT REPORT ---'.
01 WS-AUDIT-SUMMARY.
05 FILLER PIC X(20) VALUE 'Tests: '.
05 AL-TESTS PIC Z(9)9.
05 FILLER PIC X(10) VALUE ' Pass: '.
05 AL-PASS PIC Z(9)9.
05 FILLER PIC X(10) VALUE ' Fail: '.
05 AL-FAIL PIC Z(9)9.
01 WS-AUDIT-TOTAL.
05 FILLER PIC X(20) VALUE 'Hash Input: '.
05 AL-HASH-IN PIC Z(14)9.
05 FILLER PIC X(15) VALUE ' Hash Out: '.
05 AL-HASH-OUT PIC Z(14)9.
01 WS-AUDIT-CALL.
05 FILLER PIC X(10) VALUE ' CALL #'.
05 AC-CALL-NUM PIC Z(9).
05 FILLER PIC X(05) VALUE ' PGM='.
05 AC-PGM PIC X(08).
05 FILLER PIC X(05) VALUE ' A='.
05 AC-A PIC Z(9)9.
05 FILLER PIC X(05) VALUE ' B='.
05 AC-B PIC Z(9)9.
05 FILLER PIC X(10) VALUE ' RESULT='.
05 AC-RESULT PIC Z(9)9.
05 FILLER PIC X(10) VALUE ' STATUS='.
05 AC-STATUS PIC Z(9).
01 WS-AUDIT-EXCEPTION.
05 FILLER PIC X(15) VALUE ' EXCEPTION: '.
05 AE-DESC PIC X(60).
*> ============================================================
*> LOGGER CALL PARAMETERS
*> ============================================================
01 WS-LOG-MSG PIC X(80).
01 WS-LOG-LEVEL PIC X(01).
88 WS-LOG-INFO VALUE 'I'.
88 WS-LOG-WARN VALUE 'W'.
88 WS-LOG-ERROR VALUE 'E'.
01 WS-LOG-RETURN PIC 9(02).
*> ============================================================
*> 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 '25-subprogram-caller'.
*> ============================================================
*> ERROR FIELDS
*> ============================================================
01 WS-ERROR-COUNT PIC 9(03) VALUE 0.
01 WS-ERROR-MSG 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).
*> ============================================================
*> WORKING VARIABLES
*> ============================================================
01 WS-I PIC 9(02).
01 WS-CALL-NUM PIC 9(02) VALUE 0.
01 WS-TELECOM-REC.
COPY "telecom/TEL-BILLING.cpy".
PROCEDURE DIVISION.
*> ============================================================
*> 1000-INIT — Initialization
*> ============================================================
1000-INIT SECTION.
1000-START.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 1000-INIT start'.
MOVE 0 TO WS-TEST-COUNT
MOVE 0 TO WS-PASS-COUNT
MOVE 0 TO WS-FAIL-COUNT
MOVE 0 TO WS-TOTAL-INPUT-A
MOVE 0 TO WS-TOTAL-INPUT-B
MOVE 0 TO WS-TOTAL-RESULT
MOVE 0 TO WS-HASH-INPUT
MOVE 0 TO WS-HASH-OUTPUT
MOVE 0 TO WS-HASH-EXPECTED
MOVE 0 TO WS-ERROR-COUNT
MOVE 0 TO WS-EXCEPTION-COUNT
MOVE 0 TO WS-CALL-NUM
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 — Open all files
*> ============================================================
2000-OPEN SECTION.
2000-START.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 2000-OPEN start'.
OPEN OUTPUT REPORT-FILE.
IF FS-REPORT NOT = '00'
MOVE 'ERROR opening REPORT-FILE, status='
TO WS-ERROR-MSG
STRING WS-ERROR-MSG FS-REPORT INTO WS-ERROR-MSG
PERFORM 6000-ERROR THRU 6000-EXIT
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
OPEN OUTPUT AUDIT-FILE.
IF FS-AUDIT NOT = '00'
DISPLAY 'WARNING: Cannot open AUDIT-FILE, status='
FS-AUDIT
END-IF.
OPEN OUTPUT ERROR-FILE.
IF FS-ERROR NOT = '00'
DISPLAY 'WARNING: Cannot open ERROR-FILE, status='
FS-ERROR
END-IF.
WRITE AUDIT-LINE FROM WS-AUDIT-HEADER.
MOVE "=== Subprogram Call Tests ===" TO REPORT-LINE.
WRITE REPORT-LINE.
MOVE SPACES TO REPORT-LINE.
WRITE REPORT-LINE.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 2000-OPEN complete'.
2000-EXIT.
EXIT.
*> ============================================================
*> 3000-PROCESS — Execute all tests
*> ============================================================
3000-PROCESS SECTION.
3000-START.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 3000-PROCESS start'.
*> Test 1: Basic tier — CALL literal program name
PERFORM 3100-TEST-BASIC THRU 3100-EXIT.
*> Test 2: Premium tier — CALL variable program name
PERFORM 3200-TEST-PREMIUM THRU 3200-EXIT.
*> Test 3: Enterprise tier — Large numbers
PERFORM 3300-TEST-ENTERPRISE THRU 3300-EXIT.
*> Test 4: Zero values — all tiers
PERFORM 3400-TEST-ZERO THRU 3400-EXIT.
*> Test 5: RETURN-CODE check
PERFORM 3500-TEST-RETURNCODE THRU 3500-EXIT.
*> Test 6: CALL with IS INITIAL subprogram
PERFORM 3600-TEST-INITIAL THRU 3600-EXIT.
*> Test 7: GOBACK from subprogram
PERFORM 3700-TEST-GOBACK THRU 3700-EXIT.
*> Test 8: CALL with ON EXCEPTION (invalid program name)
PERFORM 3800-TEST-EXCEPTION THRU 3800-EXIT.
*> Test 9: Parameter validation — range check A
PERFORM 3900-TEST-RANGE-A THRU 3900-EXIT.
*> Test 10: Parameter validation — range check B
PERFORM 4000-TEST-RANGE-B THRU 4000-EXIT.
*> Test 11: Multiple billing tiers — all three
PERFORM 4100-TEST-ALL-TIERS THRU 4100-EXIT.
*> Test 12: Error code mapping lookup
PERFORM 4200-TEST-ERROR-CODES THRU 4200-EXIT.
*> Test 13: Logger subprogram call
PERFORM 4300-TEST-LOGGER THRU 4300-EXIT.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME
' 3000-PROCESS complete — tests='
WS-TEST-COUNT ' pass=' WS-PASS-COUNT
' fail=' WS-FAIL-COUNT.
3000-EXIT.
EXIT.
*> ============================================================
*> 3100-TEST-BASIC — Basic tier, CALL literal
*> ============================================================
3100-TEST-BASIC SECTION.
3100-START.
DISPLAY '[TRACE] Test 1: Basic tier CALL literal'.
ADD 1 TO WS-TEST-NUM.
MOVE 1 TO WS-BILLING-TIER.
MOVE 100 TO WS-INPUT-A.
MOVE 200 TO WS-INPUT-B.
MOVE 0 TO WS-RESULT.
MOVE 0 TO WS-STATUS.
MOVE 0 TO WS-ERROR-CODE.
PERFORM 5000-VALIDATE-PARAMS THRU 5000-EXIT.
IF WS-VALID-A-OK = 'Y' AND WS-VALID-B-OK = 'Y'
AND WS-VALID-TIER-OK = 'Y'
CALL 'SUBPGM' USING WS-INPUT-A, WS-INPUT-B,
WS-RESULT, WS-STATUS,
WS-ERROR-CODE, WS-BILLING-TIER
END-CALL
END-IF.
*> Expected: (100 + 200) * 1.00 (basic) = 300
MOVE 300 TO WS-EXPECTED.
PERFORM 5100-VERIFY-RESULT THRU 5100-EXIT.
MOVE "Basic tier CALL literal" TO VL-DESC.
PERFORM 5200-WRITE-VERIFY THRU 5200-EXIT.
PERFORM 5300-LOG-CALL THRU 5300-EXIT.
3100-EXIT.
EXIT.
*> ============================================================
*> 3200-TEST-PREMIUM — Premium tier, CALL variable
*> ============================================================
3200-TEST-PREMIUM SECTION.
3200-START.
DISPLAY '[TRACE] Test 2: Premium tier CALL variable'.
ADD 1 TO WS-TEST-NUM.
MOVE 2 TO WS-BILLING-TIER.
MOVE 50 TO WS-INPUT-A.
MOVE 75 TO WS-INPUT-B.
MOVE 0 TO WS-RESULT.
MOVE 0 TO WS-STATUS.
MOVE 0 TO WS-ERROR-CODE.
PERFORM 5000-VALIDATE-PARAMS THRU 5000-EXIT.
IF WS-VALID-A-OK = 'Y' AND WS-VALID-B-OK = 'Y'
AND WS-VALID-TIER-OK = 'Y'
CALL WS-CALLEE-PGM USING WS-INPUT-A, WS-INPUT-B,
WS-RESULT, WS-STATUS,
WS-ERROR-CODE, WS-BILLING-TIER
END-CALL
END-IF.
*> Expected: (50 + 75) * 1.50 (premium) = 187
MOVE 187 TO WS-EXPECTED.
PERFORM 5100-VERIFY-RESULT THRU 5100-EXIT.
MOVE "Premium tier CALL variable" TO VL-DESC.
PERFORM 5200-WRITE-VERIFY THRU 5200-EXIT.
PERFORM 5300-LOG-CALL THRU 5300-EXIT.
3200-EXIT.
EXIT.
*> ============================================================
*> 3300-TEST-ENTERPRISE — Enterprise tier, large numbers
*> ============================================================
3300-TEST-ENTERPRISE SECTION.
3300-START.
DISPLAY '[TRACE] Test 3: Enterprise tier large numbers'.
ADD 1 TO WS-TEST-NUM.
MOVE 3 TO WS-BILLING-TIER.
MOVE 99999 TO WS-INPUT-A.
MOVE 1 TO WS-INPUT-B.
MOVE 0 TO WS-RESULT.
MOVE 0 TO WS-STATUS.
MOVE 0 TO WS-ERROR-CODE.
PERFORM 5000-VALIDATE-PARAMS THRU 5000-EXIT.
IF WS-VALID-A-OK = 'Y' AND WS-VALID-B-OK = 'Y'
AND WS-VALID-TIER-OK = 'Y'
CALL 'SUBPGM' USING WS-INPUT-A, WS-INPUT-B,
WS-RESULT, WS-STATUS,
WS-ERROR-CODE, WS-BILLING-TIER
END-CALL
END-IF.
*> Expected: (99999 + 1) * 2.00 (enterprise) = 200000
MOVE 200000 TO WS-EXPECTED.
PERFORM 5100-VERIFY-RESULT THRU 5100-EXIT.
MOVE "Enterprise tier large number" TO VL-DESC.
PERFORM 5200-WRITE-VERIFY THRU 5200-EXIT.
PERFORM 5300-LOG-CALL THRU 5300-EXIT.
3300-EXIT.
EXIT.
*> ============================================================
*> 3400-TEST-ZERO — Zero values
*> ============================================================
3400-TEST-ZERO SECTION.
3400-START.
DISPLAY '[TRACE] Test 4: Zero values'.
ADD 1 TO WS-TEST-NUM.
MOVE 1 TO WS-BILLING-TIER.
MOVE 0 TO WS-INPUT-A.
MOVE 0 TO WS-INPUT-B.
MOVE 0 TO WS-RESULT.
MOVE 0 TO WS-STATUS.
MOVE 0 TO WS-ERROR-CODE.
PERFORM 5000-VALIDATE-PARAMS THRU 5000-EXIT.
IF WS-VALID-A-OK = 'Y' AND WS-VALID-B-OK = 'Y'
AND WS-VALID-TIER-OK = 'Y'
CALL 'SUBPGM' USING WS-INPUT-A, WS-INPUT-B,
WS-RESULT, WS-STATUS,
WS-ERROR-CODE, WS-BILLING-TIER
END-CALL
END-IF.
MOVE 0 TO WS-EXPECTED.
PERFORM 5100-VERIFY-RESULT THRU 5100-EXIT.
MOVE "Zero values" TO VL-DESC.
PERFORM 5200-WRITE-VERIFY THRU 5200-EXIT.
PERFORM 5300-LOG-CALL THRU 5300-EXIT.
3400-EXIT.
EXIT.
*> ============================================================
*> 3500-TEST-RETURNCODE — RETURN-CODE check
*> ============================================================
3500-TEST-RETURNCODE SECTION.
3500-START.
DISPLAY '[TRACE] Test 5: RETURN-CODE check'.
ADD 1 TO WS-TEST-NUM.
MOVE 1 TO WS-BILLING-TIER.
MOVE 10 TO WS-INPUT-A.
MOVE 20 TO WS-INPUT-B.
MOVE 0 TO WS-RESULT.
MOVE 0 TO WS-STATUS.
MOVE 0 TO WS-ERROR-CODE.
PERFORM 5000-VALIDATE-PARAMS THRU 5000-EXIT.
IF WS-VALID-A-OK = 'Y' AND WS-VALID-B-OK = 'Y'
AND WS-VALID-TIER-OK = 'Y'
CALL 'SUBPGM' USING WS-INPUT-A, WS-INPUT-B,
WS-RESULT, WS-STATUS,
WS-ERROR-CODE, WS-BILLING-TIER
END-CALL
END-IF.
DISPLAY " RETURN-CODE = " RETURN-CODE.
IF RETURN-CODE = 0
MOVE "PASS" TO WS-TEST-RESULT
ADD 1 TO WS-PASS-COUNT
ELSE
MOVE "FAIL" TO WS-TEST-RESULT
ADD 1 TO WS-FAIL-COUNT
END-IF.
ADD 1 TO WS-TEST-COUNT.
MOVE "RETURN-CODE check" TO VL-DESC.
PERFORM 5200-WRITE-VERIFY THRU 5200-EXIT.
PERFORM 5300-LOG-CALL THRU 5300-EXIT.
3500-EXIT.
EXIT.
*> ============================================================
*> 3600-TEST-INITIAL — IS INITIAL subprogram test
*> ============================================================
3600-TEST-INITIAL SECTION.
3600-START.
DISPLAY '[TRACE] Test 6: IS INITIAL subprogram'.
ADD 1 TO WS-TEST-NUM.
MOVE 1 TO WS-BILLING-TIER.
*> First call
MOVE 40 TO WS-INPUT-A.
MOVE 60 TO WS-INPUT-B.
CALL 'SUBPGM' USING WS-INPUT-A, WS-INPUT-B,
WS-RESULT, WS-STATUS,
WS-ERROR-CODE, WS-BILLING-TIER
END-CALL.
DISPLAY " First call result=" WS-RESULT.
*> Second call — subprogram should reinitialize
MOVE 4 TO WS-INPUT-A.
MOVE 6 TO WS-INPUT-B.
CALL 'SUBPGM' USING WS-INPUT-A, WS-INPUT-B,
WS-RESULT, WS-STATUS,
WS-ERROR-CODE, WS-BILLING-TIER
END-CALL.
DISPLAY " Second call result=" WS-RESULT.
MOVE 10 TO WS-EXPECTED.
PERFORM 5100-VERIFY-RESULT THRU 5100-EXIT.
MOVE "IS INITIAL subprogram" TO VL-DESC.
PERFORM 5200-WRITE-VERIFY THRU 5200-EXIT.
PERFORM 5300-LOG-CALL THRU 5300-EXIT.
3600-EXIT.
EXIT.
*> ============================================================
*> 3700-TEST-GOBACK — GOBACK from subprogram
*> ============================================================
3700-TEST-GOBACK SECTION.
3700-START.
DISPLAY '[TRACE] Test 7: GOBACK from subprogram'.
ADD 1 TO WS-TEST-NUM.
MOVE 1 TO WS-BILLING-TIER.
MOVE 77 TO WS-INPUT-A.
MOVE 23 TO WS-INPUT-B.
MOVE 0 TO WS-RESULT.
MOVE 0 TO WS-STATUS.
MOVE 0 TO WS-ERROR-CODE.
PERFORM 5000-VALIDATE-PARAMS THRU 5000-EXIT.
IF WS-VALID-A-OK = 'Y' AND WS-VALID-B-OK = 'Y'
AND WS-VALID-TIER-OK = 'Y'
CALL 'SUBPGM' USING WS-INPUT-A, WS-INPUT-B,
WS-RESULT, WS-STATUS,
WS-ERROR-CODE, WS-BILLING-TIER
END-CALL
END-IF.
MOVE 100 TO WS-EXPECTED.
PERFORM 5100-VERIFY-RESULT THRU 5100-EXIT.
MOVE "GOBACK from subprogram" TO VL-DESC.
PERFORM 5200-WRITE-VERIFY THRU 5200-EXIT.
PERFORM 5300-LOG-CALL THRU 5300-EXIT.
3700-EXIT.
EXIT.
*> ============================================================
*> 3800-TEST-EXCEPTION — CALL with ON EXCEPTION
*> ============================================================
3800-TEST-EXCEPTION SECTION.
3800-START.
DISPLAY '[TRACE] Test 8: CALL with ON EXCEPTION'.
ADD 1 TO WS-TEST-NUM.
MOVE 1 TO WS-BILLING-TIER.
MOVE 10 TO WS-INPUT-A.
MOVE 20 TO WS-INPUT-B.
MOVE 0 TO WS-RESULT.
MOVE 0 TO WS-STATUS.
MOVE 0 TO WS-ERROR-CODE.
MOVE 'OK' TO WS-CALL-RESULT.
*> Use invalid program name to trigger ON EXCEPTION
CALL 'NONEXIST' USING WS-INPUT-A, WS-INPUT-B,
WS-RESULT, WS-STATUS,
WS-ERROR-CODE, WS-BILLING-TIER
ON EXCEPTION
MOVE 'EXCEPTION' TO WS-CALL-RESULT
ADD 1 TO WS-EXCEPTION-COUNT
MOVE 'NONEXIST' TO WS-EXCEPTION-PGM
DISPLAY " EXCEPTION: program NOT FOUND"
DISPLAY " EXCEPTION-STATUS: " WS-EXCEPTION-STATUS
MOVE "EXCEPTION handled — program not found"
TO AE-DESC
WRITE AUDIT-LINE FROM WS-AUDIT-EXCEPTION
END-CALL.
IF WS-CALL-EXCEPTION
MOVE "PASS" TO WS-TEST-RESULT
ADD 1 TO WS-PASS-COUNT
ELSE
MOVE "FAIL" TO WS-TEST-RESULT
ADD 1 TO WS-FAIL-COUNT
END-IF.
ADD 1 TO WS-TEST-COUNT.
MOVE "CALL ON EXCEPTION" TO VL-DESC.
PERFORM 5200-WRITE-VERIFY THRU 5200-EXIT.
PERFORM 5300-LOG-CALL THRU 5300-EXIT.
3800-EXIT.
EXIT.
*> ============================================================
*> 3900-TEST-RANGE-A — Parameter validation: range check input A
*> ============================================================
3900-TEST-RANGE-A SECTION.
3900-START.
DISPLAY '[TRACE] Test 9: Range check input A (out of range)'.
ADD 1 TO WS-TEST-NUM.
MOVE 1 TO WS-BILLING-TIER.
*> Input A exceeds max 99999
MOVE 100000 TO WS-INPUT-A.
MOVE 100 TO WS-INPUT-B.
MOVE 0 TO WS-RESULT.
MOVE 0 TO WS-STATUS.
MOVE 0 TO WS-ERROR-CODE.
PERFORM 5000-VALIDATE-PARAMS THRU 5000-EXIT.
IF WS-VALID-A-OK = 'Y'
CALL 'SUBPGM' USING WS-INPUT-A, WS-INPUT-B,
WS-RESULT, WS-STATUS,
WS-ERROR-CODE, WS-BILLING-TIER
END-CALL
MOVE "PASS" TO WS-TEST-RESULT
ADD 1 TO WS-PASS-COUNT
ELSE
MOVE 'N' TO WS-VALID-A-OK
MOVE "FAIL" TO WS-TEST-RESULT
ADD 1 TO WS-FAIL-COUNT
DISPLAY " Range validation triggered: A=" WS-INPUT-A
" exceeds max"
MOVE "Range check caught overflow" TO AE-DESC
WRITE AUDIT-LINE FROM WS-AUDIT-EXCEPTION
END-IF.
ADD 1 TO WS-TEST-COUNT.
MOVE "Range check input A" TO VL-DESC.
PERFORM 5200-WRITE-VERIFY THRU 5200-EXIT.
3900-EXIT.
EXIT.
*> ============================================================
*> 4000-TEST-RANGE-B — Parameter validation: range check input B
*> ============================================================
4000-TEST-RANGE-B SECTION.
4000-START.
DISPLAY '[TRACE] Test 10: Range check input B (valid)'.
ADD 1 TO WS-TEST-NUM.
MOVE 1 TO WS-BILLING-TIER.
*> Input B at boundary
MOVE 50 TO WS-INPUT-A.
MOVE 99999 TO WS-INPUT-B.
MOVE 0 TO WS-RESULT.
MOVE 0 TO WS-STATUS.
MOVE 0 TO WS-ERROR-CODE.
PERFORM 5000-VALIDATE-PARAMS THRU 5000-EXIT.
IF WS-VALID-A-OK = 'Y' AND WS-VALID-B-OK = 'Y'
AND WS-VALID-TIER-OK = 'Y'
CALL 'SUBPGM' USING WS-INPUT-A, WS-INPUT-B,
WS-RESULT, WS-STATUS,
WS-ERROR-CODE, WS-BILLING-TIER
END-CALL
END-IF.
MOVE 100049 TO WS-EXPECTED.
PERFORM 5100-VERIFY-RESULT THRU 5100-EXIT.
MOVE "Range check input B valid" TO VL-DESC.
PERFORM 5200-WRITE-VERIFY THRU 5200-EXIT.
PERFORM 5300-LOG-CALL THRU 5300-EXIT.
4000-EXIT.
EXIT.
*> ============================================================
*> 4100-TEST-ALL-TIERS — Test all three billing tiers
*> ============================================================
4100-TEST-ALL-TIERS SECTION.
4100-START.
DISPLAY '[TRACE] Test 11: All billing tiers comparison'.
ADD 1 TO WS-TEST-NUM.
*> Test each tier with same input
PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > 3
MOVE WS-I TO WS-BILLING-TIER
MOVE 100 TO WS-INPUT-A
MOVE 200 TO WS-INPUT-B
MOVE 0 TO WS-RESULT
MOVE 0 TO WS-STATUS
MOVE 0 TO WS-ERROR-CODE
PERFORM 5000-VALIDATE-PARAMS
THRU 5000-EXIT
IF WS-VALID-A-OK = 'Y' AND WS-VALID-B-OK = 'Y'
AND WS-VALID-TIER-OK = 'Y'
CALL 'SUBPGM' USING WS-INPUT-A, WS-INPUT-B,
WS-RESULT, WS-STATUS,
WS-ERROR-CODE, WS-BILLING-TIER
END-CALL
END-IF
EVALUATE WS-I
WHEN 1 MOVE "Basic " TO WS-TIER-DESC
WHEN 2 MOVE "Premium " TO WS-TIER-DESC
WHEN 3 MOVE "Enterprise" TO WS-TIER-DESC
END-EVALUATE
DISPLAY " Tier " WS-I " (" WS-TIER-DESC
") result=" WS-RESULT
MOVE WS-I TO DL-TIER
PERFORM 5300-LOG-CALL THRU 5300-EXIT
END-PERFORM.
MOVE "PASS" TO WS-TEST-RESULT.
ADD 1 TO WS-PASS-COUNT.
ADD 1 TO WS-TEST-COUNT.
MOVE "All billing tiers tested" TO VL-DESC.
PERFORM 5200-WRITE-VERIFY THRU 5200-EXIT.
4100-EXIT.
EXIT.
*> ============================================================
*> 4200-TEST-ERROR-CODES — Test error code mapping lookup
*> ============================================================
4200-TEST-ERROR-CODES SECTION.
4200-START.
DISPLAY '[TRACE] Test 12: Error code mapping lookup'.
ADD 1 TO WS-TEST-NUM.
*> Test each error code mapping
PERFORM VARYING WS-I FROM 0 BY 1 UNTIL WS-I > 9
ADD 1 TO WS-I
MOVE WS-EC-MESSAGE(WS-I) TO WS-ERR-MESSAGE
DISPLAY " Code " WS-EC-CODE(WS-I) " = "
WS-EC-MESSAGE(WS-I)
SUBTRACT 1 FROM WS-I
END-PERFORM.
MOVE "PASS" TO WS-TEST-RESULT.
ADD 1 TO WS-PASS-COUNT.
ADD 1 TO WS-TEST-COUNT.
MOVE "Error code mapping verified" TO VL-DESC.
PERFORM 5200-WRITE-VERIFY THRU 5200-EXIT.
4200-EXIT.
EXIT.
*> ============================================================
*> 4300-TEST-LOGGER — Logger subprogram call
*> ============================================================
4300-TEST-LOGGER SECTION.
4300-START.
DISPLAY '[TRACE] Test 13: Logger subprogram call'.
ADD 1 TO WS-TEST-NUM.
MOVE 'I' TO WS-LOG-LEVEL.
MOVE 'Test 13: Logger subprogram invoked successfully'
TO WS-LOG-MSG.
*> Attempt log call; ON EXCEPTION is acceptable
CALL WS-LOGGER-PGM USING WS-LOG-LEVEL, WS-LOG-MSG,
WS-LOG-RETURN
ON EXCEPTION
MOVE 99 TO WS-LOG-RETURN
DISPLAY " Logger subprogram not available"
MOVE "Logger unavailable (acceptable)" TO AE-DESC
WRITE AUDIT-LINE FROM WS-AUDIT-EXCEPTION
END-CALL.
MOVE "PASS" TO WS-TEST-RESULT.
ADD 1 TO WS-PASS-COUNT.
ADD 1 TO WS-TEST-COUNT.
MOVE "Logger subprogram call" TO VL-DESC.
PERFORM 5200-WRITE-VERIFY THRU 5200-EXIT.
4300-EXIT.
EXIT.
*> ============================================================
*> 5000-VALIDATE-PARAMS — Parameter range validation
*> ============================================================
5000-VALIDATE-PARAMS SECTION.
5000-START.
MOVE 'Y' TO WS-VALID-A-OK.
MOVE 'Y' TO WS-VALID-B-OK.
MOVE 'Y' TO WS-VALID-TIER-OK.
*> Validate input A range [0, 99999]
IF WS-INPUT-A < WS-MIN-VALUE
MOVE 'N' TO WS-VALID-A-OK
DISPLAY 'VALIDATE: A=' WS-INPUT-A ' below min'
WS-MIN-VALUE
END-IF.
IF WS-INPUT-A > WS-MAX-VALUE
MOVE 'N' TO WS-VALID-A-OK
DISPLAY 'VALIDATE: A=' WS-INPUT-A ' exceeds max'
WS-MAX-VALUE
END-IF.
*> Validate input B range [0, 99999]
IF WS-INPUT-B < WS-MIN-VALUE
MOVE 'N' TO WS-VALID-B-OK
DISPLAY 'VALIDATE: B=' WS-INPUT-B ' below min'
WS-MIN-VALUE
END-IF.
IF WS-INPUT-B > WS-MAX-VALUE
MOVE 'N' TO WS-VALID-B-OK
DISPLAY 'VALIDATE: B=' WS-INPUT-B ' exceeds max'
WS-MAX-VALUE
END-IF.
*> Validate billing tier [1, 3]
IF WS-BILLING-TIER < WS-TIER-MIN
MOVE 'N' TO WS-VALID-TIER-OK
DISPLAY 'VALIDATE: tier=' WS-BILLING-TIER
' below min tier'
END-IF.
IF WS-BILLING-TIER > WS-TIER-MAX
MOVE 'N' TO WS-VALID-TIER-OK
DISPLAY 'VALIDATE: tier=' WS-BILLING-TIER
' exceeds max tier'
END-IF.
DISPLAY '[TRACE] Validate: A=' WS-VALID-A-OK
' B=' WS-VALID-B-OK
' Tier=' WS-VALID-TIER-OK.
5000-EXIT.
EXIT.
*> ============================================================
*> 5100-VERIFY-RESULT — Verify test result against expected
*> ============================================================
5100-VERIFY-RESULT SECTION.
5100-START.
ADD WS-INPUT-A TO WS-TOTAL-INPUT-A.
ADD WS-INPUT-B TO WS-TOTAL-INPUT-B.
ADD WS-RESULT TO WS-TOTAL-RESULT.
ADD WS-INPUT-A TO WS-HASH-INPUT.
ADD WS-INPUT-B TO WS-HASH-INPUT.
ADD WS-RESULT TO WS-HASH-OUTPUT.
ADD WS-EXPECTED TO WS-HASH-EXPECTED.
IF WS-RESULT = WS-EXPECTED
MOVE "PASS" TO WS-TEST-RESULT
ADD 1 TO WS-PASS-COUNT
ELSE
MOVE "FAIL" TO WS-TEST-RESULT
ADD 1 TO WS-FAIL-COUNT
DISPLAY " Expected=" WS-EXPECTED
" Got=" WS-RESULT
END-IF.
ADD 1 TO WS-TEST-COUNT.
MOVE WS-INPUT-A TO DL-A.
MOVE WS-INPUT-B TO DL-B.
MOVE WS-RESULT TO DL-RESULT.
MOVE WS-STATUS TO DL-STATUS.
MOVE WS-BILLING-TIER TO DL-TIER.
DISPLAY WS-DETAIL-LINE.
5100-EXIT.
EXIT.
*> ============================================================
*> 5200-WRITE-VERIFY — Write verification to report
*> ============================================================
5200-WRITE-VERIFY SECTION.
5200-START.
MOVE WS-TEST-NUM TO VL-NUM.
MOVE WS-TEST-RESULT TO VL-RESULT.
DISPLAY WS-VERIFY-LINE.
MOVE WS-VERIFY-LINE TO REPORT-LINE.
WRITE REPORT-LINE.
MOVE WS-DETAIL-LINE TO REPORT-LINE.
WRITE REPORT-LINE.
5200-EXIT.
EXIT.
*> ============================================================
*> 5300-LOG-CALL — Log a call to audit file
*> ============================================================
5300-LOG-CALL SECTION.
5300-START.
ADD 1 TO WS-CALL-NUM.
MOVE WS-CALL-NUM TO AC-CALL-NUM.
MOVE 'SUBPGM' TO AC-PGM.
MOVE WS-INPUT-A TO AC-A.
MOVE WS-INPUT-B TO AC-B.
MOVE WS-RESULT TO AC-RESULT.
MOVE WS-STATUS TO AC-STATUS.
WRITE AUDIT-LINE FROM WS-AUDIT-CALL.
5300-EXIT.
EXIT.
*> ============================================================
*> 6000-ERROR — Error handler
*> ============================================================
6000-ERROR SECTION.
6000-START.
ADD 1 TO WS-ERROR-COUNT.
MOVE WS-ERROR-COUNT TO ED-NUM.
MOVE WS-ERROR-MSG TO ED-MESSAGE.
DISPLAY WS-ERROR-DETAIL.
WRITE ERROR-LINE FROM WS-ERROR-DETAIL.
6000-EXIT.
EXIT.
*> ============================================================
*> 7000-AUDIT — Trace entry
*> ============================================================
7000-AUDIT SECTION.
7000-START.
ACCEPT WS-CURRENT-TIME FROM TIME.
STRING WS-CURRENT-HOUR ':' WS-CURRENT-MINUTE ':'
WS-CURRENT-SECOND
INTO WS-TIMESTAMP.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' audit point '
WS-TIMESTAMP.
7000-EXIT.
EXIT.
*> ============================================================
*> 8000-REPORT — Final report generation
*> ============================================================
8000-REPORT SECTION.
8000-START.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 8000-REPORT start'.
MOVE SPACES TO REPORT-LINE.
WRITE REPORT-LINE.
MOVE "=== Summary ===" TO REPORT-LINE.
WRITE REPORT-LINE.
MOVE WS-TEST-COUNT TO AL-TESTS.
MOVE WS-PASS-COUNT TO AL-PASS.
MOVE WS-FAIL-COUNT TO AL-FAIL.
WRITE AUDIT-LINE FROM WS-AUDIT-SUMMARY.
MOVE WS-HASH-INPUT TO AL-HASH-IN.
MOVE WS-HASH-OUTPUT TO AL-HASH-OUT.
WRITE AUDIT-LINE FROM WS-AUDIT-TOTAL.
DISPLAY "25-subprogram: Tests=" WS-TEST-COUNT
" Pass=" WS-PASS-COUNT " Fail=" WS-FAIL-COUNT.
IF WS-FAIL-COUNT > 0
MOVE "SOME TESTS FAILED — see report" TO REPORT-LINE
ELSE
MOVE "ALL TESTS PASSED" TO REPORT-LINE
END-IF.
WRITE REPORT-LINE.
WRITE AUDIT-LINE FROM WS-AUDIT-FOOTER.
8000-EXIT.
EXIT.
*> ============================================================
*> 9000-EXIT — Cleanup and close
*> ============================================================
9000-EXIT SECTION.
9000-START.
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' 9000-EXIT start'.
CLOSE REPORT-FILE.
IF FS-REPORT NOT = '00'
DISPLAY 'WARNING: REPORT-FILE close status=' FS-REPORT
END-IF.
CLOSE AUDIT-FILE.
IF FS-AUDIT NOT = '00'
DISPLAY 'WARNING: AUDIT-FILE close status=' FS-AUDIT
END-IF.
CLOSE ERROR-FILE.
IF FS-ERROR NOT = '00'
DISPLAY 'WARNING: ERROR-FILE close status=' FS-ERROR
END-IF.
DISPLAY "Report written to sub-report.txt".
IF WS-ERROR-COUNT > 0
DISPLAY 'Errors=' WS-ERROR-COUNT
' — see error-report-25.txt'
END-IF.
DISPLAY "25-subprogram: PASS".
DISPLAY '[TRACE] ' WS-PROGRAM-NAME ' END'.
STOP RUN.
9000-EXIT-EXIT.
EXIT.
END PROGRAM Main25Subprogram.
@@ -0,0 +1,756 @@
*> ============================================================
*> subprogram : 计费子程序 (Billing Subprogram) — CALLEE
*> Input : LK-A, LK-B (LINKAGE参数: 用量值)
*> LK-TIER (LINKAGE参数: 计费阶梯)
*> Output: LK-RESULT, LK-STATUS (计算结果+状态)
*> LK-ERROR-CODE (错误编码)
*> Coverage: C-N001~N008, C-A001, C-R001, C-R002
*>
*> EXPANDED: Multiple billing tiers with configurable multipliers,
*> parameter validation (range checks, numeric checks),
*> error code mapping with lookup, audit file with detail lines,
*> statistical tracking (min/max/avg), discount/premium stacking,
*> voice/data/message component breakdown, tracing, SECTION structure.
*> ============================================================
IDENTIFICATION DIVISION.
PROGRAM-ID. SUBPGM.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT AUDIT-FILE ASSIGN TO "subpgm-audit.txt"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS FS-AUDIT.
SELECT ERROR-FILE ASSIGN TO "subpgm-errors.txt"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS FS-ERROR.
SELECT CONFIG-FILE ASSIGN TO "subpgm-config.dat"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS FS-CONFIG.
DATA DIVISION.
FILE SECTION.
FD AUDIT-FILE.
01 AUDIT-LINE PIC X(120).
FD ERROR-FILE.
01 ERROR-LINE PIC X(120).
FD CONFIG-FILE.
01 CONFIG-LINE PIC X(80).
WORKING-STORAGE SECTION.
*> ============================================================
*> FILE STATUS FIELDS
*> ============================================================
01 FS-AUDIT PIC X(02).
01 FS-ERROR PIC X(02).
01 FS-CONFIG PIC X(02).
*> ============================================================
*> LOCAL STATE
*> ============================================================
01 WS-LOCAL-COUNT PIC 9(05) VALUE 0.
01 WS-PGM-NAME PIC X(08) VALUE 'SUBPGM '.
01 WS-CALL-COUNT PIC 9(05) VALUE 0.
01 WS-INIT-FLAG PIC X(01) VALUE 'N'.
88 WS-INITIALIZED VALUE 'Y'.
01 WS-SUB-STATUS PIC X(01) VALUE 'N'.
88 WS-SUB-ACTIVE VALUE 'Y'.
*> ============================================================
*> BILLING TIER CONFIGURATION
*> ============================================================
01 WS-TIER-CONFIG.
05 WS-TIER-MULTIPLIER PIC 9(02)V99.
05 WS-TIER-NAME PIC X(15).
05 WS-TIER-BASE-RATE PIC 9(03)V99.
05 WS-TIER-DATA-RATE PIC 9(03)V99.
05 WS-TIER-DISCOUNT PIC 9(02).
01 WS-TIER-TABLE.
05 FILLER PIC X(32) VALUE '011.00Basic 005.005.00 00'.
05 FILLER PIC X(32) VALUE '021.50Premium 008.007.00 05'.
05 FILLER PIC X(32) VALUE '033.00Enterprise 010.008.00 10'.
05 FILLER PIC X(32) VALUE '042.50Family 007.006.00 08'.
01 WS-TIER-TABLE-REDEF REDEFINES WS-TIER-TABLE.
05 WS-TIER-ENTRY OCCURS 4 TIMES.
10 WS-TIER-CODE PIC 9(01).
10 WS-TIER-MULT PIC 9(02)V99.
10 WS-TIER-DESC PIC X(15).
10 WS-TIER-BASE PIC 9(03)V99.
10 WS-TIER-DATA PIC 9(03)V99.
10 WS-TIER-DISC PIC 9(02).
*> ============================================================
*> CALCULATION COMPONENTS
*> ============================================================
01 WS-CALC-COMPONENTS.
05 WS-COMP-VOICE-AMT PIC 9(09)V99 VALUE 0.
05 WS-COMP-DATA-AMT PIC 9(09)V99 VALUE 0.
05 WS-COMP-MSG-AMT PIC 9(09)V99 VALUE 0.
05 WS-COMP-DISCOUNT PIC 9(09)V99 VALUE 0.
05 WS-COMP-SUBTOTAL PIC 9(09)V99 VALUE 0.
05 WS-COMP-FINAL PIC 9(09)V99 VALUE 0.
05 WS-COMP-TIER-MULT PIC 9(02)V99 VALUE 1.00.
*> ============================================================
*> CALL STATISTICS
*> ============================================================
01 WS-STATISTICS.
05 WS-STAT-MIN-VAL PIC 9(10) VALUE 9999999999.
05 WS-STAT-MAX-VAL PIC 9(10) VALUE 0.
05 WS-STAT-SUM-VAL PIC 9(15) VALUE 0.
05 WS-STAT-COUNT PIC 9(05) VALUE 0.
05 WS-STAT-AVG-VAL PIC 9(10) VALUE 0.
05 WS-STAT-ERR-COUNT PIC 9(05) VALUE 0.
05 WS-STAT-SUCCESS-CNT PIC 9(05) VALUE 0.
*> ============================================================
*> ERROR CODE MAPPING
*> ============================================================
01 WS-ERR-CODE-TABLE.
05 FILLER PIC X(32) VALUE '00OK '.
05 FILLER PIC X(32) VALUE '01INVALID INPUT A '.
05 FILLER PIC X(32) VALUE '02INVALID INPUT B '.
05 FILLER PIC X(32) VALUE '03INPUT OUT OF RANGE '.
05 FILLER PIC X(32) VALUE '04INVALID BILLING TIER '.
05 FILLER PIC X(32) VALUE '05CALCULATION OVERFLOW '.
05 FILLER PIC X(32) VALUE '06SUBPROGRAM NOT FOUND '.
05 FILLER PIC X(32) VALUE '07SUBPROGRAM ERROR '.
05 FILLER PIC X(32) VALUE '08TIER MULTIPLIER ERROR '.
05 FILLER PIC X(32) VALUE '09INTERNAL ERROR '.
01 WS-ERR-REDEF REDEFINES WS-ERR-CODE-TABLE.
05 WS-ERR-ENTRY OCCURS 10 TIMES.
10 WS-EC-CODE PIC X(02).
10 WS-EC-MESSAGE PIC X(30).
*> ============================================================
*> CALCULATION FIELDS
*> ============================================================
01 WS-CALC-SUM PIC 9(10) VALUE 0.
01 WS-CALC-RESULT PIC 9(10) VALUE 0.
01 WS-CALC-MULT PIC 9(05)V99 VALUE 0.
01 WS-OVERFLOW-CHECK PIC 9(15) VALUE 0.
01 WS-CALC-VOICE-MIN PIC 9(05) VALUE 0.
01 WS-CALC-DATA-MB PIC 9(09) VALUE 0.
01 WS-CALC-MSG-COUNT PIC 9(05) VALUE 0.
*> ============================================================
*> VALIDATION CONTROLS
*> ============================================================
01 WS-MIN-VALUE PIC 9(05) VALUE 0.
01 WS-MAX-VALUE PIC 9(05) VALUE 99999.
01 WS-MIN-TIER PIC 9(01) VALUE 1.
01 WS-MAX-TIER PIC 9(01) VALUE 4.
01 WS-VALIDATION-MSG PIC X(40).
*> ============================================================
*> AUDIT / TRACE 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).
*> ============================================================
*> ERROR 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 'SUBPGM ERR '.
05 ED-NUM PIC Z(9).
05 FILLER PIC X(02) VALUE ': '.
05 ED-MESSAGE PIC X(80).
*> ============================================================
*> AUDIT LINE TEMPLATES
*> ============================================================
01 WS-AUDIT-HEADER.
05 FILLER PIC X(40) VALUE
'=== SUBPGM Callee Audit Log ==='.
01 WS-AUDIT-CALL.
05 FILLER PIC X(10) VALUE ' CALL #'.
05 AC-CALL-NUM PIC Z(9)5.
05 FILLER PIC X(05) VALUE ' A='.
05 AC-A PIC Z(9)5.
05 FILLER PIC X(05) VALUE ' B='.
05 AC-B PIC Z(9)5.
05 FILLER PIC X(10) VALUE ' TIER='.
05 AC-TIER PIC Z(9).
05 FILLER PIC X(10) VALUE ' RESULT='.
05 AC-RESULT PIC Z(9)9.
05 FILLER PIC X(10) VALUE ' STATUS='.
05 AC-STATUS PIC Z(9).
05 FILLER PIC X(10) VALUE ' ERR='.
05 AC-ERR PIC Z(9).
01 WS-AUDIT-DETAIL.
05 FILLER PIC X(10) VALUE ' DETAIL '.
05 AD-COMP PIC X(10).
05 FILLER PIC X(05) VALUE ' AMT='.
05 AD-AMT PIC Z(11)9.99.
01 WS-AUDIT-STATS.
05 FILLER PIC X(10) VALUE ' STATS: '.
05 FILLER PIC X(10) VALUE 'MIN='.
05 AS-MIN PIC Z(9)9.
05 FILLER PIC X(10) VALUE ' MAX='.
05 AS-MAX PIC Z(9)9.
05 FILLER PIC X(10) VALUE ' AVG='.
05 AS-AVG PIC Z(9)9.
05 FILLER PIC X(10) VALUE ' CNT='.
05 AS-CNT PIC Z(9)5.
01 WS-AUDIT-ERR-STATS.
05 FILLER PIC X(10) VALUE ' ERRORS:'.
05 AE-COUNT PIC Z(9)5.
05 FILLER PIC X(10) VALUE ' SUCCESS:'.
05 AE-SUCCESS PIC Z(9)5.
01 WS-AUDIT-FOOTER.
05 FILLER PIC X(50) VALUE
'--- END OF SUBPGM AUDIT LOG ---'.
LINKAGE SECTION.
01 LK-A PIC 9(05).
01 LK-B PIC 9(05).
01 LK-RESULT PIC 9(10).
01 LK-STATUS PIC 9(02).
01 LK-ERROR-CODE PIC 9(02).
01 LK-TIER PIC 9(01).
PROCEDURE DIVISION USING LK-A, LK-B, LK-RESULT, LK-STATUS,
LK-ERROR-CODE, LK-TIER.
*> ============================================================
*> 1000-INIT — Initialization
*> ============================================================
1000-INIT SECTION.
1000-START.
ADD 1 TO WS-LOCAL-COUNT.
ADD 1 TO WS-CALL-COUNT.
ACCEPT WS-CURRENT-TIME FROM TIME.
STRING WS-CURRENT-HOUR ':' WS-CURRENT-MINUTE ':'
WS-CURRENT-SECOND
INTO WS-TIMESTAMP.
DISPLAY '[TRACE] SUBPGM 1000-INIT call #' WS-CALL-COUNT
' at ' WS-TIMESTAMP.
*> Open files only on first call
IF NOT WS-INITIALIZED
SET WS-INITIALIZED TO TRUE
SET WS-SUB-ACTIVE TO TRUE
OPEN OUTPUT AUDIT-FILE
IF FS-AUDIT NOT = '00'
DISPLAY 'SUBPGM: WARNING Cannot open AUDIT-FILE '
'status=' FS-AUDIT
ELSE
WRITE AUDIT-LINE FROM WS-AUDIT-HEADER
END-IF
OPEN OUTPUT ERROR-FILE
IF FS-ERROR NOT = '00'
DISPLAY 'SUBPGM: WARNING Cannot open ERROR-FILE '
'status=' FS-ERROR
END-IF
*> Attempt to load config file (optional)
OPEN INPUT CONFIG-FILE
IF FS-CONFIG = '00'
DISPLAY 'SUBPGM: Config file loaded'
PERFORM 1100-LOAD-CONFIG
THRU 1100-LOAD-CONFIG-EXIT
CLOSE CONFIG-FILE
ELSE
DISPLAY 'SUBPGM: No config file — using defaults'
END-IF
DISPLAY '[TRACE] SUBPGM initialized, tier table ready'
END-IF.
DISPLAY '[TRACE] SUBPGM call #' WS-CALL-COUNT
' A=' LK-A ' B=' LK-B ' TIER=' LK-TIER.
1000-EXIT.
EXIT.
*> ============================================================
*> 1100-LOAD-CONFIG — Load configuration file
*> ============================================================
1100-LOAD-CONFIG SECTION.
1100-LOAD-CONFIG-START.
DISPLAY '[TRACE] SUBPGM 1100-LOAD-CONFIG start'.
*> Read config lines (format: TIER,NAME,MULT,BASE,DATA,DISC)
READ CONFIG-FILE INTO CONFIG-LINE
AT END DISPLAY 'SUBPGM: Config file empty'
END-READ.
DISPLAY '[TRACE] SUBPGM 1100-LOAD-CONFIG complete'.
1100-LOAD-CONFIG-EXIT.
EXIT.
*> ============================================================
*> 2000-VALIDATE — Validate all inputs
*> ============================================================
2000-VALIDATE SECTION.
2000-START.
DISPLAY '[TRACE] SUBPGM 2000-VALIDATE A=' LK-A
' B=' LK-B ' TIER=' LK-TIER.
*> Validate input A: must be numeric
IF LK-A IS NOT NUMERIC
MOVE 01 TO LK-ERROR-CODE
MOVE 99 TO LK-STATUS
STRING 'INPUT A NOT NUMERIC: value=' LK-A
INTO WS-VALIDATION-MSG
DISPLAY 'SUBPGM: ' WS-VALIDATION-MSG
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
GOBACK
END-IF.
*> Validate input A: range check
IF LK-A < WS-MIN-VALUE
MOVE 03 TO LK-ERROR-CODE
MOVE 99 TO LK-STATUS
STRING 'INPUT A BELOW MIN: ' LK-A ' < ' WS-MIN-VALUE
INTO WS-VALIDATION-MSG
DISPLAY 'SUBPGM: ' WS-VALIDATION-MSG
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
GOBACK
END-IF.
IF LK-A > WS-MAX-VALUE
MOVE 03 TO LK-ERROR-CODE
MOVE 99 TO LK-STATUS
STRING 'INPUT A EXCEEDS MAX: ' LK-A ' > ' WS-MAX-VALUE
INTO WS-VALIDATION-MSG
DISPLAY 'SUBPGM: ' WS-VALIDATION-MSG
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
GOBACK
END-IF.
*> Validate input B: must be numeric
IF LK-B IS NOT NUMERIC
MOVE 02 TO LK-ERROR-CODE
MOVE 99 TO LK-STATUS
STRING 'INPUT B NOT NUMERIC: value=' LK-B
INTO WS-VALIDATION-MSG
DISPLAY 'SUBPGM: ' WS-VALIDATION-MSG
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
GOBACK
END-IF.
*> Validate input B: range check
IF LK-B < WS-MIN-VALUE
MOVE 03 TO LK-ERROR-CODE
MOVE 99 TO LK-STATUS
STRING 'INPUT B BELOW MIN: ' LK-B ' < ' WS-MIN-VALUE
INTO WS-VALIDATION-MSG
DISPLAY 'SUBPGM: ' WS-VALIDATION-MSG
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
GOBACK
END-IF.
IF LK-B > WS-MAX-VALUE
MOVE 03 TO LK-ERROR-CODE
MOVE 99 TO LK-STATUS
STRING 'INPUT B EXCEEDS MAX: ' LK-B ' > ' WS-MAX-VALUE
INTO WS-VALIDATION-MSG
DISPLAY 'SUBPGM: ' WS-VALIDATION-MSG
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
GOBACK
END-IF.
*> Validate tier: must be numeric
IF LK-TIER IS NOT NUMERIC
MOVE 04 TO LK-ERROR-CODE
MOVE 99 TO LK-STATUS
STRING 'TIER NOT NUMERIC: value=' LK-TIER
INTO WS-VALIDATION-MSG
DISPLAY 'SUBPGM: ' WS-VALIDATION-MSG
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
GOBACK
END-IF.
*> Validate tier: range check
IF LK-TIER < WS-MIN-TIER
MOVE 04 TO LK-ERROR-CODE
MOVE 99 TO LK-STATUS
STRING 'TIER ' LK-TIER ' BELOW MIN ' WS-MIN-TIER
INTO WS-VALIDATION-MSG
DISPLAY 'SUBPGM: ' WS-VALIDATION-MSG
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
GOBACK
END-IF.
IF LK-TIER > WS-MAX-TIER
MOVE 04 TO LK-ERROR-CODE
MOVE 99 TO LK-STATUS
STRING 'TIER ' LK-TIER ' EXCEEDS MAX ' WS-MAX-TIER
INTO WS-VALIDATION-MSG
DISPLAY 'SUBPGM: ' WS-VALIDATION-MSG
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
GOBACK
END-IF.
DISPLAY '[TRACE] SUBPGM 2000-VALIDATE all checks passed'.
2000-EXIT.
EXIT.
*> ============================================================
*> 3000-CALCULATE — Perform billing calculation with components
*> ============================================================
3000-CALCULATE SECTION.
3000-START.
DISPLAY '[TRACE] SUBPGM 3000-CALCULATE start'
' A=' LK-A ' B=' LK-B.
*> Step 1: Decompose input A into billing components
*> Input A represents voice minutes (high-order) + msg count
DIVIDE LK-A BY 1000 GIVING WS-CALC-VOICE-MIN
REMAINDER WS-CALC-MSG-COUNT.
*> Input B represents data volume in MB
MOVE LK-B TO WS-CALC-DATA-MB.
DISPLAY '[TRACE] SUBPGM 3000-CALC components: voice='
WS-CALC-VOICE-MIN ' data=' WS-CALC-DATA-MB
' msg=' WS-CALC-MSG-COUNT.
*> Step 2: Look up tier configuration from table
MOVE 1.00 TO WS-COMP-TIER-MULT.
MOVE 'Basic' TO WS-TIER-NAME.
MOVE 5.00 TO WS-TIER-BASE-RATE.
MOVE 5.00 TO WS-TIER-DATA-RATE.
MOVE 0 TO WS-TIER-DISCOUNT.
MOVE 0 TO WS-CALC-SUM.
IF LK-TIER >= 1 AND LK-TIER <= 4
MOVE WS-TIER-MULT(LK-TIER) TO WS-COMP-TIER-MULT
MOVE WS-TIER-DESC(LK-TIER) TO WS-TIER-NAME
MOVE WS-TIER-BASE(LK-TIER) TO WS-TIER-BASE-RATE
MOVE WS-TIER-DATA(LK-TIER) TO WS-TIER-DATA-RATE
MOVE WS-TIER-DISC(LK-TIER) TO WS-TIER-DISCOUNT
DISPLAY '[TRACE] SUBPGM tier=' WS-TIER-NAME
' mult=' WS-COMP-TIER-MULT
' base-rate=' WS-TIER-BASE-RATE
' data-rate=' WS-TIER-DATA-RATE
' disc=' WS-TIER-DISCOUNT '%'
ELSE
MOVE 04 TO LK-ERROR-CODE
DISPLAY 'SUBPGM: Invalid tier — using defaults'
END-IF.
*> Step 3: Calculate voice amount
COMPUTE WS-COMP-VOICE-AMT =
WS-CALC-VOICE-MIN * WS-TIER-BASE-RATE.
DISPLAY '[TRACE] SUBPGM voice amt=' WS-COMP-VOICE-AMT.
*> Step 4: Calculate data amount
COMPUTE WS-COMP-DATA-AMT =
WS-CALC-DATA-MB * WS-TIER-DATA-RATE.
DISPLAY '[TRACE] SUBPGM data amt=' WS-COMP-DATA-AMT.
*> Step 5: Calculate messaging amount (flat 2.00 per msg)
COMPUTE WS-COMP-MSG-AMT =
WS-CALC-MSG-COUNT * 2.00.
DISPLAY '[TRACE] SUBPGM msg amt=' WS-COMP-MSG-AMT.
*> Step 6: Calculate subtotal (voice + data + msg)
COMPUTE WS-COMP-SUBTOTAL =
WS-COMP-VOICE-AMT + WS-COMP-DATA-AMT + WS-COMP-MSG-AMT.
DISPLAY '[TRACE] SUBPGM subtotal=' WS-COMP-SUBTOTAL.
*> Step 7: Apply tier discount
IF WS-TIER-DISCOUNT > 0
COMPUTE WS-COMP-DISCOUNT =
WS-COMP-SUBTOTAL * WS-TIER-DISCOUNT / 100
COMPUTE WS-COMP-FINAL =
WS-COMP-SUBTOTAL - WS-COMP-DISCOUNT
DISPLAY '[TRACE] SUBPGM discount=' WS-COMP-DISCOUNT
' (' WS-TIER-DISCOUNT '%)'
ELSE
MOVE 0 TO WS-COMP-DISCOUNT
MOVE WS-COMP-SUBTOTAL TO WS-COMP-FINAL
END-IF.
*> Step 8: Apply tier multiplier
COMPUTE WS-CALC-MULT = WS-COMP-FINAL * WS-COMP-TIER-MULT.
*> Step 9: Check for overflow
MOVE WS-CALC-MULT TO WS-OVERFLOW-CHECK.
IF WS-OVERFLOW-CHECK > 9999999999
MOVE 05 TO LK-ERROR-CODE
MOVE 98 TO LK-STATUS
MOVE 9999999999 TO WS-CALC-RESULT
DISPLAY 'SUBPGM: OVERFLOW final=' WS-COMP-FINAL
' mult=' WS-COMP-TIER-MULT
PERFORM 6000-ERROR THRU 6000-ERROR-EXIT
GOBACK
END-IF.
*> Step 10: Round and store result
COMPUTE WS-CALC-RESULT ROUNDED = WS-CALC-MULT.
MOVE WS-CALC-RESULT TO LK-RESULT.
*> Step 11: Compute original legacy sum for backward compatibility
COMPUTE WS-CALC-SUM = LK-A + LK-B.
*> Step 12: Set success status
MOVE 00 TO LK-STATUS.
MOVE 00 TO LK-ERROR-CODE.
MOVE 0 TO RETURN-CODE.
*> Step 13: Update statistics
ADD 1 TO WS-STAT-COUNT.
ADD 1 TO WS-STAT-SUCCESS-CNT.
ADD LK-RESULT TO WS-STAT-SUM-VAL.
IF LK-RESULT < WS-STAT-MIN-VAL
MOVE LK-RESULT TO WS-STAT-MIN-VAL
END-IF.
IF LK-RESULT > WS-STAT-MAX-VAL
MOVE LK-RESULT TO WS-STAT-MAX-VAL
END-IF.
COMPUTE WS-STAT-AVG-VAL =
WS-STAT-SUM-VAL / WS-STAT-COUNT.
DISPLAY ' SUBPGM: A=' LK-A ' B=' LK-B
' SUM(legacy)=' WS-CALC-SUM
' TIER=' LK-TIER ' (' WS-TIER-NAME ')'
' MULT=' WS-COMP-TIER-MULT
' DISCOUNT=' WS-TIER-DISCOUNT '%'
' VOICE=' WS-COMP-VOICE-AMT
' DATA=' WS-COMP-DATA-AMT
' MSG=' WS-COMP-MSG-AMT
' RESULT=' LK-RESULT
' CALLS=' WS-LOCAL-COUNT.
3000-EXIT.
EXIT.
*> ============================================================
*> 4000-LOG — Comprehensive audit logging
*> ============================================================
4000-LOG SECTION.
4000-START.
DISPLAY '[TRACE] SUBPGM 4000-LOG call #' WS-CALL-COUNT.
*> Write main call record
MOVE WS-CALL-COUNT TO AC-CALL-NUM.
MOVE LK-A TO AC-A.
MOVE LK-B TO AC-B.
MOVE LK-TIER TO AC-TIER.
MOVE LK-RESULT TO AC-RESULT.
MOVE LK-STATUS TO AC-STATUS.
MOVE LK-ERROR-CODE TO AC-ERR.
WRITE AUDIT-LINE FROM WS-AUDIT-CALL.
*> Write component breakdown
MOVE 'Voice ' TO AD-COMP.
MOVE WS-COMP-VOICE-AMT TO AD-AMT.
WRITE AUDIT-LINE FROM WS-AUDIT-DETAIL.
MOVE 'Data ' TO AD-COMP.
MOVE WS-COMP-DATA-AMT TO AD-AMT.
WRITE AUDIT-LINE FROM WS-AUDIT-DETAIL.
MOVE 'Messaging ' TO AD-COMP.
MOVE WS-COMP-MSG-AMT TO AD-AMT.
WRITE AUDIT-LINE FROM WS-AUDIT-DETAIL.
MOVE 'Discount ' TO AD-COMP.
MOVE WS-COMP-DISCOUNT TO AD-AMT.
WRITE AUDIT-LINE FROM WS-AUDIT-DETAIL.
MOVE 'Final ' TO AD-COMP.
MOVE WS-COMP-FINAL TO AD-AMT.
WRITE AUDIT-LINE FROM WS-AUDIT-DETAIL.
*> Write tier info
MOVE 'Tier Info ' TO AD-COMP.
STRING WS-TIER-NAME ' mult=' WS-COMP-TIER-MULT
INTO AD-COMP.
MOVE 0 TO AD-AMT.
WRITE AUDIT-LINE FROM WS-AUDIT-DETAIL.
4000-EXIT.
EXIT.
*> ============================================================
*> 5000-STATS — Update and optionally report statistics
*> ============================================================
5000-STATS SECTION.
5000-START.
DISPLAY '[TRACE] SUBPGM 5000-STATS call #' WS-CALL-COUNT.
*> Write statistics every 5 calls
IF WS-CALL-COUNT / 5 * 5 = WS-CALL-COUNT
MOVE WS-STAT-MIN-VAL TO AS-MIN
MOVE WS-STAT-MAX-VAL TO AS-MAX
MOVE WS-STAT-AVG-VAL TO AS-AVG
MOVE WS-STAT-COUNT TO AS-CNT
WRITE AUDIT-LINE FROM WS-AUDIT-STATS
MOVE WS-STAT-ERR-COUNT TO AE-COUNT
MOVE WS-STAT-SUCCESS-CNT TO AE-SUCCESS
WRITE AUDIT-LINE FROM WS-AUDIT-ERR-STATS
END-IF.
5000-EXIT.
EXIT.
*> ============================================================
*> 5500-DISCOUNT-CALC — Additional discount stacking logic
*> ============================================================
5500-DISCOUNT-CALC SECTION.
5500-START.
DISPLAY '[TRACE] SUBPGM 5500-DISCOUNT-CALC'
' tier=' LK-TIER.
*> Apply additional volume-based discount
*> If combined voice+data exceeds threshold, extra discount
IF WS-COMP-SUBTOTAL > 10000
COMPUTE WS-COMP-DISCOUNT =
WS-COMP-DISCOUNT +
(WS-COMP-SUBTOTAL * 5 / 100)
DISPLAY '[TRACE] SUBPGM volume discount applied: +5%'
END-IF.
*> If enterprise tier and high data usage, waive data fees
IF LK-TIER = 3 AND WS-CALC-DATA-MB > 500
COMPUTE WS-COMP-DATA-AMT = 0
DISPLAY '[TRACE] SUBPGM enterprise data waiver applied'
END-IF.
*> Recalculate final after additional discounts
COMPUTE WS-COMP-FINAL =
WS-COMP-VOICE-AMT + WS-COMP-DATA-AMT + WS-COMP-MSG-AMT
- WS-COMP-DISCOUNT.
IF WS-COMP-FINAL < 0
MOVE 0 TO WS-COMP-FINAL
END-IF.
DISPLAY '[TRACE] SUBPGM after extra discounts: final='
WS-COMP-FINAL.
5500-EXIT.
EXIT.
*> ============================================================
*> 6000-ERROR — Error handler with error code lookup
*> ============================================================
6000-ERROR SECTION.
6000-START.
ADD 1 TO WS-ERROR-COUNT.
ADD 1 TO WS-STAT-ERR-COUNT.
MOVE WS-ERROR-COUNT TO ED-NUM.
*> Look up error code description
IF LK-ERROR-CODE >= 0 AND LK-ERROR-CODE <= 9
ADD 1 TO LK-ERROR-CODE
MOVE WS-EC-MESSAGE(LK-ERROR-CODE) TO ED-MESSAGE
SUBTRACT 1 FROM LK-ERROR-CODE
ELSE
STRING 'Error code=' LK-ERROR-CODE
' A=' LK-A ' B=' LK-B ' tier=' LK-TIER
INTO ED-MESSAGE
END-IF.
DISPLAY WS-ERROR-DETAIL.
WRITE ERROR-LINE FROM WS-ERROR-DETAIL.
6000-EXIT.
EXIT.
*> ============================================================
*> 7000-CLEANUP — Close files and write final stats
*> ============================================================
7000-CLEANUP SECTION.
7000-START.
DISPLAY '[TRACE] SUBPGM 7000-CLEANUP'
' call #' WS-CALL-COUNT.
*> Write final statistics
MOVE WS-STAT-MIN-VAL TO AS-MIN.
MOVE WS-STAT-MAX-VAL TO AS-MAX.
MOVE WS-STAT-AVG-VAL TO AS-AVG.
MOVE WS-STAT-COUNT TO AS-CNT.
WRITE AUDIT-LINE FROM WS-AUDIT-STATS.
MOVE WS-STAT-ERR-COUNT TO AE-COUNT.
MOVE WS-STAT-SUCCESS-CNT TO AE-SUCCESS.
WRITE AUDIT-LINE FROM WS-AUDIT-ERR-STATS.
WRITE AUDIT-LINE FROM WS-AUDIT-FOOTER.
*> Close files when count >= 15
IF WS-CALL-COUNT >= 15
CLOSE AUDIT-FILE
IF FS-AUDIT NOT = '00' AND NOT = '00'
DISPLAY 'SUBPGM: WARNING closing AUDIT status='
FS-AUDIT
END-IF
CLOSE ERROR-FILE
IF FS-ERROR NOT = '00' AND NOT = '00'
DISPLAY 'SUBPGM: WARNING closing ERROR status='
FS-ERROR
END-IF
DISPLAY '[TRACE] SUBPGM files closed cleanly'
END-IF.
7000-EXIT.
EXIT.
*> ============================================================
*> 8000-REPORT — Summary report generation
*> ============================================================
8000-REPORT SECTION.
8000-START.
DISPLAY '[TRACE] SUBPGM 8000-REPORT call #' WS-CALL-COUNT.
DISPLAY ' SUBPGM CALL SUMMARY:'
DISPLAY ' Total calls : ' WS-STAT-COUNT
DISPLAY ' Successful : ' WS-STAT-SUCCESS-CNT
DISPLAY ' Errors : ' WS-STAT-ERR-COUNT
DISPLAY ' Min result : ' WS-STAT-MIN-VAL
DISPLAY ' Max result : ' WS-STAT-MAX-VAL
DISPLAY ' Avg result : ' WS-STAT-AVG-VAL
DISPLAY ' Sum result : ' WS-STAT-SUM-VAL.
8000-EXIT.
EXIT.
*> ============================================================
*> 9000-EXIT — Return to caller with GOBACK
*> ============================================================
9000-EXIT SECTION.
9000-START.
DISPLAY '[TRACE] SUBPGM 9000-EXIT call #' WS-CALL-COUNT.
*> Call PERFORM to log, update stats, and cleanup
PERFORM 4000-LOG THRU 4000-LOG-EXIT.
PERFORM 5000-STATS THRU 5000-STATS-EXIT.
PERFORM 5500-DISCOUNT-CALC THRU 5500-DISCOUNT-CALC-EXIT.
PERFORM 7000-CLEANUP THRU 7000-CLEANUP-EXIT.
DISPLAY '[TRACE] SUBPGM returning to caller, result='
LK-RESULT ' status=' LK-STATUS.
GOBACK.
9000-EXIT-EXIT.
EXIT.