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 @@
AB00000000000000000000000000001C00000000000001D0000000000000100101E00001F0000001G000000001BB00000000000000000000000000002C00000000000002D0000000000000200102E00002F0000002G000000002CB00000000000000000000000000003C00000000000003D0000000000000300103E00003F0000003G000000003DB00000000000000000000000000004C00000000000004D0000000000000400104E00004F0000004G000000004EB00000000000000000000000000005C00000000000005D0000000000000500105E00005F0000005G000000005FB00000000000000000000000000006C00000000000006D0000000000000600106E00006F0000006G000000006GB00000000000000000000000000007C00000000000007D0000000000000700107E00007F0000007G000000007HB00000000000000000000000000008C00000000000008D0000000000000800108E00008F0000008G000000008IB00000000000000000000000000009C00000000000009D0000000000000900109E00009F0000009G000000009JB00000000000000000000000000010C00000000000010D0000000000001000110E00010F0000010G000000010KB00000000000000000000000000011C00000000000011D0000000000001100111E00011F0000011G000000011LB00000000000000000000000000012C00000000000012D0000000000001200112E00012F0000012G000000012MB00000000000000000000000000013C00000000000013D0000000000001300113E00013F0000013G000000013NB00000000000000000000000000014C00000000000014D0000000000001400114E00014F0000014G000000014OB00000000000000000000000000015C00000000000015D0000000000001500115E00015F0000015G000000015PB00000000000000000000000000016C00000000000016D0000000000001600116E00016F0000016G000000016QB00000000000000000000000000017C00000000000017D0000000000001700117E00017F0000017G000000017RB00000000000000000000000000018C00000000000018D0000000000001800118E00018F0000018G000000018SB00000000000000000000000000019C00000000000019D0000000000001900119E00019F0000019G000000019TB00000000000000000000000000020C00000000000020D0000000000002000120E00020F0000020G000000020UB00000000000000000000000000021C00000000000021D0000000000002100121E00021F0000021G000000021VB00000000000000000000000000022C00000000000022D0000000000002200122E00022F0000022G000000022WB00000000000000000000000000023C00000000000023D0000000000002300123E00023F0000023G000000023WB00000000000000000000000000024C00000000000024D0000000000002400124E00024F0000024G000000024YB00000000000000000000000000025C00000000000025D0000000000002500125E00025F0000025G000000025ZB00000000000000000000000000026C00000000000026D0000000000002600126E00026F0000026G000000026AB00000000000000000000000000027C00000000000027D0000000000002700127E00027F0000027G000000027BB00000000000000000000000000028C00000000000028D0000000000002800128E00028F0000028G000000028CB00000000000000000000000000029C00000000000029D0000000000002900129E00029F0000029G000000029DB00000000000000000000000000030C00000000000030D0000000000003000130E00030F0000030G000000030EB00000000000000000000000000031C00000000000031D0000000000003100131E00031F0000031G000000031FB00000000000000000000000000032C00000000000032D0000000000003200132E00032F0000032G000000032GB00000000000000000000000000033C00000000000033D0000000000003300133E00033F0000033G000000033HB00000000000000000000000000034C00000000000034D0000000000003400134E00034F0000034G000000034IB00000000000000000000000000035C00000000000035D0000000000003500135E00035F0000035G000000035JB00000000000000000000000000036C00000000000036D0000000000003600136E00036F0000036G000000036KB00000000000000000000000000037C00000000000037D0000000000003700137E00037F0000037G000000037LB00000000000000000000000000038C00000000000038D0000000000003800138E00038F0000038G000000038MB00000000000000000000000000039C00000000000039D0000000000003900139E00039F0000039G000000039NB00000000000000000000000000040C00000000000040D0000000000004000140E00040F0000040G000000040OB00000000000000000000000000041C00000000000041D0000000000004100141E00041F0000041G000000041PB00000000000000000000000000042C00000000000042D0000000000004200142E00042F0000042G000000042QB00000000000000000000000000043C00000000000043D0000000000004300143E00043F0000043G000000043RB00000000000000000000000000044C00000000000044D0000000000004400144E00044F0000044G000000044SB00000000000000000000000000045C00000000000045D0000000000004500145E00045F0000045G000000045TB00000000000000000000000000046C00000000000046D0000000000004600146E00046F0000046G000000046UB00000000000000000000000000047C00000000000047D0000000000004700147E00047F0000047G000000047VB00000000000000000000000000048C00000000000048D0000000000004800148E00048F0000048G000000048WB00000000000000000000000000049C00000000000049D0000000000004900149E00049F0000049G000000049XB00000000000000000000000000050C00000000000050D0000000000005000150E00050F0000050G000000050YB00000000000000000000000000051C00000000000051D0000000000005100151E00051F0000051G000000051ZB00000000000000000000000000052C00000000000052D0000000000005200152E00052F0000052G000000052AB00000000000000000000000000053C00000000000053D0000000000005300153E00053F0000053G000000053BB00000000000000000000000000054C00000000000054D0000000000005400154E00054F0000054G000000054CB00000000000000000000000000055C00000000000055D0000000000005500155E00055F0000055G000000055DB00000000000000000000000000056C00000000000056D0000000000005600156E00056F0000056G000000056EB00000000000000000000000000057C00000000000057D0000000000005700157E00057F0000057G000000057FB00000000000000000000000000058C00000000000058D0000000000005800158E00058F0000058G000000058GB00000000000000000000000000059C00000000000059D0000000000005900159E00059F0000059G000000059HB00000000000000000000000000060C00000000000060D0000000000006000160E00060F0000060G000000060IB00000000000000000000000000061C00000000000061D0000000000006100161E00061F0000061G000000061JB00000000000000000000000000062C00000000000062D0000000000006200162E00062F0000062G000000062KB00000000000000000000000000063C00000000000063D0000000000006300163E00063F0000063G000000063LB00000000000000000000000000064C00000000000064D0000000000006400164E00064F0000064G000000064MB00000000000000000000000000065C00000000000065D0000000000006500165E00065F0000065G000000065NB00000000000000000000000000066C00000000000066D0000000000006600166E00066F0000066G000000066OB00000000000000000000000000067C00000000000067D0000000000006700167E00067F0000067G000000067PB00000000000000000000000000068C00000000000068D0000000000006800168E00068F0000068G000000068QB00000000000000000000000000069C00000000000069D0000000000006900169E00069F0000069G000000069RB00000000000000000000000000070C00000000000070D0000000000007000170E00070F0000070G000000070SB00000000000000000000000000071C00000000000071D0000000000007100171E00071F0000071G000000071TB00000000000000000000000000072C00000000000072D0000000000007200172E00072F0000072G000000072UB00000000000000000000000000073C00000000000073D0000000000007300173E00073F0000073G000000073VB00000000000000000000000000074C00000000000074D0000000000007400174E00074F0000074G000000074WB00000000000000000000000000075C00000000000075D0000000000007500175E00075F0000075G000000075XB00000000000000000000000000076C00000000000076D0000000000007600176E00076F0000076G000000076YB00000000000000000000000000077C00000000000077D0000000000007700177E00077F0000077G000000077ZB00000000000000000000000000078C00000000000078D0000000000007800178E00078F0000078G000000078AB00000000000000000000000000079C00000000000079D0000000000007900179E00079F0000079G000000079BB00000000000000000000000000080C00000000000080D0000000000008000180E00080F0000080G000000080CB00000000000000000000000000081C00000000000081D0000000000008100181E00081F0000081G000000081DB00000000000000000000000000082C00000000000082D0000000000008200182E00082F0000082G000000082EB00000000000000000000000000083C00000000000083D0000000000008300183E00083F0000083G000000083FB00000000000000000000000000084C00000000000084D0000000000008400184E00084F0000084G000000084GB00000000000000000000000000085C00000000000085D0000000000008500185E00085F0000085G000000085HB00000000000000000000000000086C00000000000086D0000000000008600186E00086F0000086G000000086IB00000000000000000000000000087C00000000000087D0000000000008700187E00087F0000087G000000087JB00000000000000000000000000088C00000000000088D0000000000008800188E00088F0000088G000000088KB00000000000000000000000000089C00000000000089D0000000000008900189E00089F0000089G000000089LB00000000000000000000000000090C00000000000090D0000000000009000190E00090F0000090G000000090MB00000000000000000000000000091C00000000000091D0000000000009100191E00091F0000091G000000091NB00000000000000000000000000092C00000000000092D0000000000009200192E00092F0000092G000000092OB00000000000000000000000000093C00000000000093D0000000000009300193E00093F0000093G000000093PB00000000000000000000000000094C00000000000094D0000000000009400194E00094F0000094G000000094QB00000000000000000000000000095C00000000000095D0000000000009500195E00095F0000095G000000095RB00000000000000000000000000096C00000000000096D0000000000009600196E00096F0000096G000000096SB00000000000000000000000000097C00000000000097D0000000000009700197E00097F0000097G000000097TB00000000000000000000000000098C00000000000098D0000000000009800198E00098F0000098G000000098UB00000000000000000000000000099C00000000000099D0000000000009900199E00099F0000099G000000099VB00000000000000000000000000100C00000000000100D0000000000010000200E00100F0000100G000000100WB00000000000000000000000000101C00000000000101D0000000000010100201E00101F0000101G000000101XB00000000000000000000000000102C00000000000102D0000000000010200202E00102F0000102G000000102YB00000000000000000000000000103C00000000000103D0000000000010300203E00103F0000103G000000103ZB00000000000000000000000000104C00000000000104D0000000000010400204E00104F0000104G000000104AB00000000000000000000000000105C00000000000105D0000000000010500205E00105F0000105G000000105BB00000000000000000000000000106C00000000000106D0000000000010600206E00106F0000106G000000106CB00000000000000000000000000107C00000000000107D0000000000010700207E00107F0000107G000000107DB00000000000000000000000000108C00000000000108D0000000000010800208E00108F0000108G000000108EB00000000000000000000000000109C00000000000109D0000000000010900209E00109F0000109G000000109FB00000000000000000000000000110C00000000000110D0000000000011000210E00110F0000110G000000110GB00000000000000000000000000111C00000000000111D0000000000011100211E00111F0000111G000000111HB00000000000000000000000000112C00000000000112D0000000000011200212E00112F0000112G000000112IB00000000000000000000000000113C00000000000113D0000000000011300213E00113F0000113G000000113JB00000000000000000000000000114C00000000000114D0000000000011400214E00114F0000114G000000114KB00000000000000000000000000115C00000000000115D0000000000011500215E00115F0000115G000000115LB00000000000000000000000000116C00000000000116D0000000000011600216E00116F0000116G000000116MB00000000000000000000000000117C00000000000117D0000000000011700217E00117F0000117G000000117NB00000000000000000000000000118C00000000000118D0000000000011800218E00118F0000118G000000118OB00000000000000000000000000119C00000000000119D0000000000011900219E00119F0000119G000000119PB00000000000000000000000000120C00000000000120D0000000000012000220E00120F0000120G000000120QB00000000000000000000000000121C00000000000121D0000000000012100221E00121F0000121G000000121RB00000000000000000000000000122C00000000000122D0000000000012200222E00122F0000122G000000122SB00000000000000000000000000123C00000000000123D0000000000012300223E00123F0000123G000000123
@@ -0,0 +1,62 @@
# 13-validation-nodup: Field Validation (No Duplicates)
## 电信业务场景
CDR字段校验。对CDR记录进行字段值校验,检查通话类型代码是否为有效值(01/02/03),有效记录输出到GOOD文件,无效记录输出到BAD文件。
## Description
Reads FILE-IN and validates FIELD1 against an allowed-value list
('A', 'B', 'C', 'D', 'E'). Records with valid FIELD1 values are
written to FILE-OUT-GOOD. Invalid records are written to
FILE-OUT-BAD with an error code appended.
## Record Layout
### Input / Good Output (31 bytes)
| Field | Type | Length | Description |
|--------|----------|--------|---------------------------|
| FIELD1 | PIC X | 1 | Validation field |
| FIELD2 | PIC X | 30 | Description / payload |
### Bad Output (33 bytes)
| Field | Type | Length | Description |
|----------|----------|--------|---------------------------|
| FIELD1 | PIC X | 1 | Original invalid value |
| FIELD2 | PIC X | 30 | Original description |
| ERR-CODE | PIC X | 2 | Error code ('01') |
## Files
| File | Purpose |
|---------------------------|----------------------------------|
| main-13-validation-nodup.cbl | Main COBOL program |
| data-gen.sh | Generate test data |
| run.sh | Compile, run, verify |
| README.md | This file |
## Tests
| Test Case | Description |
|----------------------------|-----------------------------------------|
| Valid values A-E | All 5 allowed values pass to GOOD |
| Invalid values X,Y,Z | Non-allowed alpha values go to BAD |
| Invalid values 1,9 | Numeric values go to BAD |
| Empty FIELD1 (space) | Treated as invalid, goes to BAD |
| Error code in BAD output | '01' appended to each bad record |
## Usage
```bash
cd 13-validation-nodup
bash data-gen.sh
bash run.sh
```
## Expected Behavior
- 5 valid records in FILE-OUT-GOOD (31 bytes each = 155 bytes)
- 6 invalid records in FILE-OUT-BAD (33 bytes each = 198 bytes)
- Invalid records contain the original data plus '01' error code
@@ -0,0 +1 @@
Audit Report - ValidationNodup Run: 2026-06-22 16:35:13 === VALIDATION AUDIT LOG === Program: ValidationNodup Batch timestamp: 2026-06-22 16:35:13 Records read : 1 Records good : 0 Records bad : 1 Valid (no err) : 0 Invalid (error): 1 Control recs :00001 Control good :00000 Control bad :00001 Hash duration : 101 Hash caller : 1555 --- Error Counts by Code --- E001 HIGH 0 Invalid call type - not in A/B/C/D/ E002 LOW 0 Call type not uppercase E003 MED 1 Caller number length not 10-15 char E004 MED 1 Caller number leading chars not num E005 HIGH 0 Duration out of valid range 0-99999 E006 HIGH 0 Duration field not numeric E007 MED 0 Start-time format invalid (not HHMM E008 MED 1 Start-time field not numeric Severity: LOW=format MED=data HIGH=critical Rules: R1A/E001 R1B/E002 R2A/E003 R2B/E004 R3A/E005 R3B/E006 R4A/E007 R4B/E008 Input : file-in.dat Output: file-out-good.dat Output: file-out-bad.dat Output: error-report.dat Output: audit-report.dat === END AUDIT LOG ===
@@ -0,0 +1 @@
Error Deta Run: 2026- Rec-ID Fi G000000001 IN-FIELD2 E003 Caller number length not 10-15 chars MED G000000001 IN-FIELD2 E004 Caller number leading chars not numeric MED G000000001 IN-START-TIME E008 Start-time field not numeric MED --- End of *** ERROR Batch: 202 Records re Records go Records ba Valid (no Invalid (w Total proc Hash durat --- Error E001 HIGH E002 LOW E003 MED E004 MED E005 HIGH E006 HIGH E007 MED E008 MED *** END OF
@@ -0,0 +1 @@
AB00000000000000000000000000001C00000000000001D0000000000000100101E00001F0000001G000000001E003MED
@@ -0,0 +1,933 @@
>>SOURCE FORMAT IS FREE
*> ============================================================
*> 13-validation-nodup : CDR字段校验 (CDR Field Validation)
*> Expanded with SECTION structure, comprehensive field-by-field
*> CDR validation, error accumulation, error detail report,
*> audit summary, batch control totals, hash totals,
*> FILE STATUS checks after every I/O, DISPLAY tracing with
*> timestamps, and error severity levels.
*> Input : FILE-IN (file-in.dat: CDR记录, 90 bytes)
*> Output: FILE-OUT-GOOD (file-out-good.dat: 校验通过)
*> FILE-OUT-BAD (file-out-bad.dat: 校验失败)
*> ERROR-REPORT (error-report.dat: 错误明细)
*> AUDIT-FILE (audit-report.dat: 审计摘要)
*> Coverage: VF-N001, VF-N002, VF-R001, VF-N007, VF-N008
*> VF-A003, VF-A004, VF-P004, VF-L002, VF-S002
*> ============================================================
IDENTIFICATION DIVISION.
PROGRAM-ID. ValidationNodup.
*>
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT FILE-IN ASSIGN TO 'file-in.dat'
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-FILE-IN-STATUS.
SELECT FILE-OUT-GOOD ASSIGN TO 'file-out-good.dat'
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-FILE-GOOD-STATUS.
SELECT FILE-OUT-BAD ASSIGN TO 'file-out-bad.dat'
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-FILE-BAD-STATUS.
SELECT ERROR-REPORT ASSIGN TO 'error-report.dat'
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-ERR-RPT-STATUS.
SELECT AUDIT-FILE ASSIGN TO 'audit-report.dat'
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-AUDIT-STATUS.
*>
DATA DIVISION.
FILE SECTION.
FD FILE-IN.
01 IN-REC.
*> Original fields (backward compatible, 31 bytes)
05 IN-FIELD1 PIC X(01).
05 IN-FIELD2 PIC X(30).
*> Extended CDR fields (additional 59 bytes = 90 total)
05 IN-CALLER-NUM PIC X(15).
05 IN-CALLEE-NUM PIC X(15).
05 IN-DURATION PIC 9(05).
05 IN-START-TIME PIC X(06).
05 IN-START-DATE PIC X(08).
05 IN-RECORD-ID PIC X(10).
*>
FD FILE-OUT-GOOD.
01 GOOD-REC.
05 GOOD-FIELD1 PIC X(01).
05 GOOD-FIELD2 PIC X(30).
05 GOOD-CALLER-NUM PIC X(15).
05 GOOD-CALLEE-NUM PIC X(15).
05 GOOD-DURATION PIC 9(05).
05 GOOD-START-TIME PIC X(06).
05 GOOD-START-DATE PIC X(08).
05 GOOD-RECORD-ID PIC X(10).
*>
FD FILE-OUT-BAD.
01 BAD-REC.
05 BAD-FIELD1 PIC X(01).
05 BAD-FIELD2 PIC X(30).
05 BAD-CALLER-NUM PIC X(15).
05 BAD-CALLEE-NUM PIC X(15).
05 BAD-DURATION PIC 9(05).
05 BAD-START-TIME PIC X(06).
05 BAD-START-DATE PIC X(08).
05 BAD-RECORD-ID PIC X(10).
05 BAD-ERR-CODE PIC X(04).
05 BAD-SEVERITY PIC X(06).
*>
FD ERROR-REPORT.
01 ERR-RPT-REC.
05 ERR-RPT-RECORD-ID PIC X(10).
05 ERR-RPT-SEP1 PIC X(01) VALUE SPACE.
05 ERR-RPT-FIELD-NAME PIC X(15).
05 ERR-RPT-SEP2 PIC X(01) VALUE SPACE.
05 ERR-RPT-ERR-CODE PIC X(04).
05 ERR-RPT-SEP3 PIC X(01) VALUE SPACE.
05 ERR-RPT-ERR-DESC PIC X(40).
05 ERR-RPT-SEP4 PIC X(01) VALUE SPACE.
05 ERR-RPT-SEVERITY PIC X(06).
*>
FD AUDIT-FILE.
01 AUDIT-REC PIC X(80).
*>
WORKING-STORAGE SECTION.
01 WS-TELECOM-REC.
COPY "telecom/TEL-BILLING.cpy".
*> File status fields
01 WS-FILE-IN-STATUS PIC X(02).
01 WS-FILE-GOOD-STATUS PIC X(02).
01 WS-FILE-BAD-STATUS PIC X(02).
01 WS-ERR-RPT-STATUS PIC X(02).
01 WS-AUDIT-STATUS PIC X(02).
*> Flags and indicators
01 WS-EOF PIC X(01) VALUE 'N'.
88 WS-EOF-YES VALUE 'Y' FALSE 'N'.
01 WS-REC-IS-VALID PIC X(01) VALUE 'Y'.
88 WS-REC-IS-VALID-YES VALUE 'Y' FALSE 'N'.
88 WS-REC-IS-VALID-NO VALUE 'N'.
01 WS-VALID-FOUND PIC X(01) VALUE 'N'.
88 WS-VALID-FOUND-YES VALUE 'Y' FALSE 'N'.
01 WS-NUM-OK PIC X(01) VALUE 'Y'.
88 WS-NUM-OK-YES VALUE 'Y' FALSE 'N'.
88 WS-NUM-OK-NO VALUE 'N'.
*> Record counts
01 WS-TOTAL-READ PIC 9(05) VALUE ZERO.
01 WS-GOOD-COUNT PIC 9(05) VALUE ZERO.
01 WS-BAD-COUNT PIC 9(05) VALUE ZERO.
01 WS-PROCESSED-COUNT PIC 9(05) VALUE ZERO.
01 WS-VALID-COUNT PIC 9(05) VALUE ZERO.
01 WS-INVALID-COUNT PIC 9(05) VALUE ZERO.
01 WS-TRACE-COUNT PIC 9(05) VALUE ZERO.
01 WS-RECORD-ID-NUM PIC 9(05) VALUE ZERO.
*> Allowed values table for call type (original logic)
01 WS-ALLOWED-VALUES.
05 WS-ALLOWED-CHAR PIC X(01) OCCURS 5 TIMES.
01 WS-IDX PIC 9(02).
01 WS-J PIC 9(02).
*> Error code definitions (8 error types E001-E008)
01 WS-ERR-DEF-TABLE.
05 WS-ERR-DEF-ENTRY OCCURS 8 TIMES.
10 WS-ED-CODE PIC X(04).
10 WS-ED-DESC PIC X(40).
10 WS-ED-SEVERITY PIC X(06).
10 WS-ED-COUNT PIC 9(05) VALUE ZERO.
01 WS-ERR-DEF-COUNT PIC 9(02) VALUE 8.
01 WS-ED-IDX PIC 9(02).
*> Per-record error accumulation table (max 10 errors)
01 WS-REC-ERROR-TABLE.
05 WS-REC-ERR OCCURS 10 TIMES.
10 WS-RE-FIELD PIC X(15).
10 WS-RE-CODE PIC X(04).
10 WS-RE-DESC PIC X(40).
10 WS-RE-SEVERITY PIC X(06).
01 WS-REC-ERR-COUNT PIC 9(02) VALUE ZERO.
01 WS-REC-ERR-IDX PIC 9(02).
*> Hash totals for data integrity
01 WS-HASH-DURATION PIC 9(12) VALUE ZERO.
01 WS-HASH-CALLER-CHARS PIC 9(12) VALUE ZERO.
01 WS-CHAR-VAL PIC 9(03).
*> Batch control totals
01 WS-CONTROL-TOTAL-REC PIC 9(05) VALUE ZERO.
01 WS-CONTROL-TOTAL-GOOD PIC 9(05) VALUE ZERO.
01 WS-CONTROL-TOTAL-BAD PIC 9(05) VALUE ZERO.
01 WS-BATCH-HASH-DUR PIC 9(12) VALUE ZERO.
*> Timestamp fields for DISPLAY tracing
01 WS-CURRENT-TIME.
05 WS-CURR-YEAR PIC X(04).
05 WS-CURR-MONTH PIC X(02).
05 WS-CURR-DAY PIC X(02).
05 WS-CURR-HOUR PIC X(02).
05 WS-CURR-MIN PIC X(02).
05 WS-CURR-SEC PIC X(02).
01 WS-TIMESTAMP PIC X(20).
01 WS-TRACE-TS PIC X(20).
*> Work fields for duration validation
01 WS-DURATION-NUM PIC 9(05).
01 WS-DURATION-DISP PIC Z(04)9.
01 WS-DURATION-STR PIC X(05).
01 WS-DURATION-CHR PIC X(01).
*> Work fields for caller number (IN-FIELD2) validation
01 WS-CALLER-TEXT PIC X(30).
01 WS-CALLER-LEN PIC 9(02).
01 WS-CALLER-TRAIL-SP PIC 9(02).
01 WS-CALLER-CHK-IDX PIC 9(02).
01 WS-CALLER-CHR PIC X(01).
*> Work fields for start-time validation
01 WS-START-TIME-STR PIC X(06).
01 WS-TIME-HH PIC 9(02).
01 WS-TIME-MM PIC 9(02).
01 WS-TIME-SS PIC 9(02).
01 WS-TIME-CHR PIC X(01).
*> Numeric check work fields
01 WS-NUM-CHR PIC X(01).
01 WS-NUM-IDX PIC 9(02).
*> Report and audit formatting fields
01 WS-ED-TOTAL PIC Z(09)9.
01 WS-ED-GOOD PIC Z(09)9.
01 WS-ED-BAD PIC Z(09)9.
01 WS-ED-VALID PIC Z(09)9.
01 WS-ED-INVALID PIC Z(09)9.
01 WS-ED-HASH PIC Z(14)9.
01 WS-ED-ERR-COUNT PIC Z(09)9.
*> Error severity
01 WS-SEVERITY PIC X(01).
88 WS-SEV-WARNING VALUE 'W'.
88 WS-SEV-ERROR VALUE 'E'.
88 WS-SEV-FATAL VALUE 'F'.
01 WS-RETURN-CODE PIC 9(02) VALUE ZERO.
*>
PROCEDURE DIVISION.
MAIN SECTION.
MB-PROCESS.
PERFORM 1000-INIT
PERFORM 2000-OPEN-FILES
PERFORM 3000-READ-INPUT UNTIL WS-EOF-YES
PERFORM 4000-REPORT
PERFORM 5000-AUDIT
PERFORM 6000-ERROR-HANDLE
PERFORM 9000-EXIT
STOP RUN.
*>
*> ============================================================
*> 1000-INIT : Initialize counters, tables, and batch header
*> ============================================================
1000-INIT SECTION.
I1000-START.
DISPLAY 'ValidationNodup: 1000-INIT starting...'.
*> Fill allowed-values table (original logic)
MOVE 'A' TO WS-ALLOWED-CHAR(1).
MOVE 'B' TO WS-ALLOWED-CHAR(2).
MOVE 'C' TO WS-ALLOWED-CHAR(3).
MOVE 'D' TO WS-ALLOWED-CHAR(4).
MOVE 'E' TO WS-ALLOWED-CHAR(5).
*> Initialize counters
MOVE ZERO TO WS-TOTAL-READ WS-GOOD-COUNT WS-BAD-COUNT
WS-PROCESSED-COUNT WS-VALID-COUNT
WS-INVALID-COUNT WS-TRACE-COUNT
WS-RECORD-ID-NUM.
*> Initialize hash and control totals
MOVE ZERO TO WS-HASH-DURATION WS-HASH-CALLER-CHARS
WS-CONTROL-TOTAL-REC WS-CONTROL-TOTAL-GOOD
WS-CONTROL-TOTAL-BAD WS-BATCH-HASH-DUR.
MOVE 'N' TO WS-EOF.
*> Populate error code definitions (8 error types)
MOVE 'E001' TO WS-ED-CODE(1).
MOVE 'Invalid call type - not in A/B/C/D/E'
TO WS-ED-DESC(1).
MOVE 'HIGH' TO WS-ED-SEVERITY(1).
MOVE 'E002' TO WS-ED-CODE(2).
MOVE 'Call type not uppercase' TO WS-ED-DESC(2).
MOVE 'LOW' TO WS-ED-SEVERITY(2).
MOVE 'E003' TO WS-ED-CODE(3).
MOVE 'Caller number length not 10-15 chars'
TO WS-ED-DESC(3).
MOVE 'MED' TO WS-ED-SEVERITY(3).
MOVE 'E004' TO WS-ED-CODE(4).
MOVE 'Caller number leading chars not numeric'
TO WS-ED-DESC(4).
MOVE 'MED' TO WS-ED-SEVERITY(4).
MOVE 'E005' TO WS-ED-CODE(5).
MOVE 'Duration out of valid range 0-99999'
TO WS-ED-DESC(5).
MOVE 'HIGH' TO WS-ED-SEVERITY(5).
MOVE 'E006' TO WS-ED-CODE(6).
MOVE 'Duration field not numeric' TO WS-ED-DESC(6).
MOVE 'HIGH' TO WS-ED-SEVERITY(6).
MOVE 'E007' TO WS-ED-CODE(7).
MOVE 'Start-time format invalid (not HHMMSS)'
TO WS-ED-DESC(7).
MOVE 'MED' TO WS-ED-SEVERITY(7).
MOVE 'E008' TO WS-ED-CODE(8).
MOVE 'Start-time field not numeric' TO WS-ED-DESC(8).
MOVE 'MED' TO WS-ED-SEVERITY(8).
*> Capture batch start timestamp
MOVE FUNCTION CURRENT-DATE TO WS-CURRENT-TIME.
STRING WS-CURR-YEAR '-' WS-CURR-MONTH '-'
WS-CURR-DAY ' ' WS-CURR-HOUR ':'
WS-CURR-MIN ':' WS-CURR-SEC INTO WS-TIMESTAMP.
DISPLAY 'ValidationNodup: Batch started at ' WS-TIMESTAMP.
DISPLAY 'ValidationNodup: 8 error definitions loaded'.
EXIT SECTION.
*>
*> ============================================================
*> 2000-OPEN-FILES : Open all 5 files with FILE STATUS checks
*> ============================================================
2000-OPEN-FILES SECTION.
I2000-START.
DISPLAY 'ValidationNodup: 2000-OPEN-FILES...'.
OPEN INPUT FILE-IN.
IF WS-FILE-IN-STATUS NOT = '00'
DISPLAY 'ERROR: Cannot open FILE-IN, status: '
WS-FILE-IN-STATUS
MOVE 1 TO RETURN-CODE STOP RUN END-IF.
DISPLAY ' FILE-IN opened status=' WS-FILE-IN-STATUS.
OPEN OUTPUT FILE-OUT-GOOD.
IF WS-FILE-GOOD-STATUS NOT = '00'
DISPLAY 'ERROR: Cannot open FILE-OUT-GOOD, status: '
WS-FILE-GOOD-STATUS
MOVE 1 TO RETURN-CODE STOP RUN END-IF.
DISPLAY ' FILE-OUT-GOOD opened status=' WS-FILE-GOOD-STATUS.
OPEN OUTPUT FILE-OUT-BAD.
IF WS-FILE-BAD-STATUS NOT = '00'
DISPLAY 'ERROR: Cannot open FILE-OUT-BAD, status: '
WS-FILE-BAD-STATUS
MOVE 1 TO RETURN-CODE STOP RUN END-IF.
DISPLAY ' FILE-OUT-BAD opened status=' WS-FILE-BAD-STATUS.
OPEN OUTPUT ERROR-REPORT.
IF WS-ERR-RPT-STATUS NOT = '00'
DISPLAY 'ERROR: Cannot open ERROR-REPORT, status: '
WS-ERR-RPT-STATUS
MOVE 1 TO RETURN-CODE STOP RUN END-IF.
DISPLAY ' ERROR-REPORT opened status=' WS-ERR-RPT-STATUS.
OPEN OUTPUT AUDIT-FILE.
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY 'ERROR: Cannot open AUDIT-FILE, status: '
WS-AUDIT-STATUS
MOVE 1 TO RETURN-CODE STOP RUN END-IF.
DISPLAY ' AUDIT-FILE opened status=' WS-AUDIT-STATUS.
*> Write report and audit headers
MOVE SPACES TO ERR-RPT-REC.
STRING 'Error Detail Report - ValidationNodup'
INTO ERR-RPT-RECORD-ID.
WRITE ERR-RPT-REC.
IF WS-ERR-RPT-STATUS NOT = '00'
DISPLAY 'ERROR: Write ERR-RPT header status='
WS-ERR-RPT-STATUS END-IF.
MOVE SPACES TO ERR-RPT-REC.
STRING 'Run: ' WS-TIMESTAMP INTO ERR-RPT-RECORD-ID.
WRITE ERR-RPT-REC.
MOVE SPACES TO ERR-RPT-REC.
STRING 'Rec-ID Field Code Description'
INTO ERR-RPT-RECORD-ID.
WRITE ERR-RPT-REC.
MOVE SPACES TO ERR-RPT-REC. WRITE ERR-RPT-REC.
*>
MOVE SPACES TO AUDIT-REC.
STRING 'Audit Report - ValidationNodup' INTO AUDIT-REC.
WRITE AUDIT-REC.
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY 'ERROR: Write AUDIT header status='
WS-AUDIT-STATUS END-IF.
MOVE SPACES TO AUDIT-REC.
STRING 'Run: ' WS-TIMESTAMP INTO AUDIT-REC.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC. WRITE AUDIT-REC.
DISPLAY 'ValidationNodup: All files opened OK'.
EXIT SECTION.
*>
*> ============================================================
*> 3000-READ-INPUT : Read loop — read record and dispatch
*> ============================================================
3000-READ-INPUT SECTION.
I3000-START.
READ FILE-IN
AT END SET WS-EOF-YES TO TRUE
DISPLAY '3000-READ-INPUT: EOF total read='
WS-TOTAL-READ
NOT AT END
ADD 1 TO WS-TOTAL-READ
ADD 1 TO WS-RECORD-ID-NUM
ADD 1 TO WS-TRACE-COUNT
*> Periodic tracing every 50 records
IF WS-TRACE-COUNT >= 50
MOVE FUNCTION CURRENT-DATE TO WS-CURRENT-TIME
STRING WS-CURR-HOUR ':' WS-CURR-MIN ':'
WS-CURR-SEC INTO WS-TRACE-TS
DISPLAY '3000-READ-INPUT: [' WS-TRACE-TS
'] #' WS-TOTAL-READ
' F1="' IN-FIELD1 '"'
MOVE ZERO TO WS-TRACE-COUNT
END-IF
IF WS-FILE-IN-STATUS NOT = '00'
DISPLAY '3000-READ-INPUT: READ status='
WS-FILE-IN-STATUS
END-IF
PERFORM 3100-VALIDATE-RECORD
PERFORM 3200-PROCESS-RECORD
END-READ.
EXIT SECTION.
*>
*> ============================================================
*> 3100-VALIDATE-RECORD : Field-by-field CDR validation
*> R1A/E001 IN-FIELD1 allowed values A-E
*> R1B/E002 IN-FIELD1 uppercase check
*> R2A/E003 IN-FIELD2 caller length 10-15
*> R2B/E004 IN-FIELD2 leading digit numeric
*> R3A/E005 IN-DURATION range 0-99999
*> R3B/E006 IN-DURATION numeric check
*> R4A/E007 IN-START-TIME HHMMSS format
*> R4B/E008 IN-START-TIME numeric check
*> Errors accumulated into WS-REC-ERROR-TABLE
*> ============================================================
3100-VALIDATE-RECORD SECTION.
I3100-START.
MOVE ZERO TO WS-REC-ERR-COUNT.
MOVE SPACES TO WS-REC-ERROR-TABLE.
MOVE 'Y' TO WS-REC-IS-VALID.
MOVE FUNCTION CURRENT-DATE TO WS-CURRENT-TIME.
STRING WS-CURR-HOUR ':' WS-CURR-MIN ':'
WS-CURR-SEC INTO WS-TRACE-TS.
DISPLAY '3100-VALIDATE: [' WS-TRACE-TS '] #'
WS-TOTAL-READ ' F1="' IN-FIELD1 '"'.
*> ---- Rule 1A: IN-FIELD1 allowed values check (original) ----
MOVE 'N' TO WS-VALID-FOUND.
PERFORM VARYING WS-IDX FROM 1 BY 1 UNTIL WS-IDX > 5
IF IN-FIELD1 = WS-ALLOWED-CHAR(WS-IDX)
SET WS-VALID-FOUND-YES TO TRUE END-IF
END-PERFORM.
IF NOT WS-VALID-FOUND-YES
ADD 1 TO WS-REC-ERR-COUNT
MOVE 'IN-FIELD1' TO WS-RE-FIELD(WS-REC-ERR-COUNT)
MOVE 'E001' TO WS-RE-CODE(WS-REC-ERR-COUNT)
MOVE 'Invalid call type - not in A/B/C/D/E'
TO WS-RE-DESC(WS-REC-ERR-COUNT)
MOVE 'HIGH' TO WS-RE-SEVERITY(WS-REC-ERR-COUNT)
MOVE 'N' TO WS-REC-IS-VALID
DISPLAY '3100-VALIDATE: E001 F1="' IN-FIELD1 '"' END-IF.
*> ---- Rule 1B: IN-FIELD1 uppercase check ----
IF IN-FIELD1 NOT >= 'A' OR IN-FIELD1 NOT <= 'Z'
IF WS-REC-ERR-COUNT < 10
ADD 1 TO WS-REC-ERR-COUNT
MOVE 'IN-FIELD1' TO WS-RE-FIELD(WS-REC-ERR-COUNT)
MOVE 'E002' TO WS-RE-CODE(WS-REC-ERR-COUNT)
MOVE 'Call type not uppercase'
TO WS-RE-DESC(WS-REC-ERR-COUNT)
MOVE 'LOW' TO WS-RE-SEVERITY(WS-REC-ERR-COUNT)
DISPLAY '3100-VALIDATE: E002 F1 not upper' END-IF
END-IF.
*> ---- Rule 2A: IN-FIELD2 caller length 10-15 ----
MOVE IN-FIELD2 TO WS-CALLER-TEXT.
MOVE ZERO TO WS-CALLER-TRAIL-SP.
INSPECT FUNCTION REVERSE(WS-CALLER-TEXT)
TALLYING WS-CALLER-TRAIL-SP FOR LEADING SPACES.
COMPUTE WS-CALLER-LEN = 30 - WS-CALLER-TRAIL-SP.
IF WS-CALLER-LEN < 10 OR WS-CALLER-LEN > 15
IF WS-REC-ERR-COUNT < 10
ADD 1 TO WS-REC-ERR-COUNT
MOVE 'IN-FIELD2' TO WS-RE-FIELD(WS-REC-ERR-COUNT)
MOVE 'E003' TO WS-RE-CODE(WS-REC-ERR-COUNT)
MOVE 'Caller number length not 10-15 chars'
TO WS-RE-DESC(WS-REC-ERR-COUNT)
MOVE 'MED' TO WS-RE-SEVERITY(WS-REC-ERR-COUNT)
MOVE 'N' TO WS-REC-IS-VALID
DISPLAY '3100-VALIDATE: E003 len=' WS-CALLER-LEN
END-IF END-IF.
*> ---- Rule 2B: IN-FIELD2 leading digits numeric ----
IF WS-CALLER-LEN >= 5 MOVE 5 TO WS-CALLER-CHK-IDX
ELSE MOVE WS-CALLER-LEN TO WS-CALLER-CHK-IDX END-IF.
IF WS-CALLER-CHK-IDX > 0
MOVE 'Y' TO WS-NUM-OK
PERFORM VARYING WS-IDX FROM 1 BY 1
UNTIL WS-IDX > WS-CALLER-CHK-IDX
MOVE IN-FIELD2(WS-IDX:1) TO WS-CALLER-CHR
IF WS-CALLER-CHR < '0' OR WS-CALLER-CHR > '9'
MOVE 'N' TO WS-NUM-OK END-IF
END-PERFORM
IF WS-NUM-OK = 'N'
IF WS-REC-ERR-COUNT < 10
ADD 1 TO WS-REC-ERR-COUNT
MOVE 'IN-FIELD2'
TO WS-RE-FIELD(WS-REC-ERR-COUNT)
MOVE 'E004'
TO WS-RE-CODE(WS-REC-ERR-COUNT)
MOVE 'Caller number leading chars not numeric'
TO WS-RE-DESC(WS-REC-ERR-COUNT)
MOVE 'MED'
TO WS-RE-SEVERITY(WS-REC-ERR-COUNT)
MOVE 'N' TO WS-REC-IS-VALID
DISPLAY '3100-VALIDATE: E004 lead not num'
END-IF END-IF END-IF.
*> ---- Rule 3A: IN-DURATION numeric check ----
MOVE IN-DURATION TO WS-DURATION-STR.
MOVE 'Y' TO WS-NUM-OK.
PERFORM VARYING WS-IDX FROM 1 BY 1 UNTIL WS-IDX > 5
MOVE WS-DURATION-STR(WS-IDX:1) TO WS-NUM-CHR
IF WS-NUM-CHR < '0' OR WS-NUM-CHR > '9'
MOVE 'N' TO WS-NUM-OK END-IF
END-PERFORM.
IF WS-NUM-OK = 'N'
IF WS-REC-ERR-COUNT < 10
ADD 1 TO WS-REC-ERR-COUNT
MOVE 'IN-DURATION' TO WS-RE-FIELD(WS-REC-ERR-COUNT)
MOVE 'E006' TO WS-RE-CODE(WS-REC-ERR-COUNT)
MOVE 'Duration field not numeric'
TO WS-RE-DESC(WS-REC-ERR-COUNT)
MOVE 'HIGH' TO WS-RE-SEVERITY(WS-REC-ERR-COUNT)
MOVE 'N' TO WS-REC-IS-VALID
DISPLAY '3100-VALIDATE: E006 dur non-num'
END-IF
END-IF.
*> ---- Rule 3B: IN-DURATION range 0-99999 ----
IF WS-NUM-OK NOT = 'N'
MOVE IN-DURATION TO WS-DURATION-NUM
IF WS-DURATION-NUM < 0 OR WS-DURATION-NUM > 99999
IF WS-REC-ERR-COUNT < 10
ADD 1 TO WS-REC-ERR-COUNT
MOVE 'IN-DURATION'
TO WS-RE-FIELD(WS-REC-ERR-COUNT)
MOVE 'E005'
TO WS-RE-CODE(WS-REC-ERR-COUNT)
MOVE 'Duration out of valid range 0-99999'
TO WS-RE-DESC(WS-REC-ERR-COUNT)
MOVE 'HIGH'
TO WS-RE-SEVERITY(WS-REC-ERR-COUNT)
MOVE 'N' TO WS-REC-IS-VALID
DISPLAY '3100-VALIDATE: E005 dur='
WS-DURATION-NUM
END-IF
END-IF
END-IF.
*> ---- Rule 4A: IN-START-TIME numeric check ----
MOVE IN-START-TIME TO WS-START-TIME-STR.
MOVE 'Y' TO WS-NUM-OK.
PERFORM VARYING WS-IDX FROM 1 BY 1 UNTIL WS-IDX > 6
MOVE WS-START-TIME-STR(WS-IDX:1) TO WS-NUM-CHR
IF WS-NUM-CHR < '0' OR WS-NUM-CHR > '9'
MOVE 'N' TO WS-NUM-OK END-IF
END-PERFORM.
IF WS-NUM-OK = 'N'
IF WS-REC-ERR-COUNT < 10
ADD 1 TO WS-REC-ERR-COUNT
MOVE 'IN-START-TIME'
TO WS-RE-FIELD(WS-REC-ERR-COUNT)
MOVE 'E008'
TO WS-RE-CODE(WS-REC-ERR-COUNT)
MOVE 'Start-time field not numeric'
TO WS-RE-DESC(WS-REC-ERR-COUNT)
MOVE 'MED'
TO WS-RE-SEVERITY(WS-REC-ERR-COUNT)
MOVE 'N' TO WS-REC-IS-VALID
DISPLAY '3100-VALIDATE: E008 time non-num'
END-IF
END-IF.
*> ---- Rule 4B: IN-START-TIME HHMMSS format ----
IF WS-NUM-OK NOT = 'N'
MOVE IN-START-TIME TO WS-START-TIME-STR
MOVE WS-START-TIME-STR(1:2) TO WS-TIME-HH
MOVE WS-START-TIME-STR(3:2) TO WS-TIME-MM
MOVE WS-START-TIME-STR(5:2) TO WS-TIME-SS
IF WS-TIME-HH > 23 OR WS-TIME-MM > 59
OR WS-TIME-SS > 59
IF WS-REC-ERR-COUNT < 10
ADD 1 TO WS-REC-ERR-COUNT
MOVE 'IN-START-TIME'
TO WS-RE-FIELD(WS-REC-ERR-COUNT)
MOVE 'E007'
TO WS-RE-CODE(WS-REC-ERR-COUNT)
MOVE 'Start-time format invalid (not HHMMSS)'
TO WS-RE-DESC(WS-REC-ERR-COUNT)
MOVE 'MED'
TO WS-RE-SEVERITY(WS-REC-ERR-COUNT)
MOVE 'N' TO WS-REC-IS-VALID
DISPLAY '3100-VALIDATE: E007 time HH='
WS-TIME-HH ' MM=' WS-TIME-MM
' SS=' WS-TIME-SS
END-IF
END-IF
END-IF.
*> Accumulate error counts into definitions table
PERFORM VARYING WS-REC-ERR-IDX FROM 1 BY 1
UNTIL WS-REC-ERR-IDX > WS-REC-ERR-COUNT
PERFORM VARYING WS-ED-IDX FROM 1 BY 1
UNTIL WS-ED-IDX > WS-ERR-DEF-COUNT
IF WS-RE-CODE(WS-REC-ERR-IDX)
= WS-ED-CODE(WS-ED-IDX)
ADD 1 TO WS-ED-COUNT(WS-ED-IDX)
END-IF
END-PERFORM
END-PERFORM.
DISPLAY '3100-VALIDATE: Done errors=' WS-REC-ERR-COUNT
' valid=' WS-REC-IS-VALID.
EXIT SECTION.
*>
*> ============================================================
*> 3200-PROCESS-RECORD : Route record and update totals
*> ============================================================
3200-PROCESS-RECORD SECTION.
I3200-START.
IF WS-REC-IS-VALID-YES
ADD 1 TO WS-GOOD-COUNT WS-VALID-COUNT
WS-CONTROL-TOTAL-GOOD
DISPLAY '3200: #' WS-TOTAL-READ ' -> GOOD'
ELSE
ADD 1 TO WS-BAD-COUNT WS-INVALID-COUNT
WS-CONTROL-TOTAL-BAD
DISPLAY '3200: #' WS-TOTAL-READ ' -> BAD errors='
WS-REC-ERR-COUNT END-IF.
ADD 1 TO WS-PROCESSED-COUNT WS-CONTROL-TOTAL-REC.
*> Hash totals
MOVE IN-DURATION TO WS-DURATION-NUM.
ADD WS-DURATION-NUM TO WS-HASH-DURATION WS-BATCH-HASH-DUR.
MOVE IN-FIELD1 TO WS-CALLER-CHR.
COMPUTE WS-CHAR-VAL = FUNCTION ORD(WS-CALLER-CHR).
ADD WS-CHAR-VAL TO WS-HASH-CALLER-CHARS.
PERFORM VARYING WS-IDX FROM 1 BY 1 UNTIL WS-IDX > 30
MOVE IN-FIELD2(WS-IDX:1) TO WS-CALLER-CHR
IF WS-CALLER-CHR NOT = SPACE
COMPUTE WS-CHAR-VAL = FUNCTION ORD(WS-CALLER-CHR)
ADD WS-CHAR-VAL TO WS-HASH-CALLER-CHARS
END-IF
END-PERFORM.
PERFORM 3300-WRITE-OUTPUT.
EXIT SECTION.
*>
*> ============================================================
*> 3300-WRITE-OUTPUT : Write records and error details
*> ============================================================
3300-WRITE-OUTPUT SECTION.
I3300-START.
MOVE IN-RECORD-ID TO ERR-RPT-RECORD-ID.
*> Write to GOOD file
IF WS-REC-IS-VALID-YES
MOVE IN-FIELD1 TO GOOD-FIELD1
MOVE IN-FIELD2 TO GOOD-FIELD2
MOVE IN-CALLER-NUM TO GOOD-CALLER-NUM
MOVE IN-CALLEE-NUM TO GOOD-CALLEE-NUM
MOVE IN-DURATION TO GOOD-DURATION
MOVE IN-START-TIME TO GOOD-START-TIME
MOVE IN-START-DATE TO GOOD-START-DATE
MOVE IN-RECORD-ID TO GOOD-RECORD-ID
WRITE GOOD-REC
IF WS-FILE-GOOD-STATUS NOT = '00'
DISPLAY '3300: WRITE GOOD failed status='
WS-FILE-GOOD-STATUS END-IF
DISPLAY '3300: Wrote GOOD #' WS-TOTAL-READ
ELSE
*> Write to BAD file
MOVE IN-FIELD1 TO BAD-FIELD1
MOVE IN-FIELD2 TO BAD-FIELD2
MOVE IN-CALLER-NUM TO BAD-CALLER-NUM
MOVE IN-CALLEE-NUM TO BAD-CALLEE-NUM
MOVE IN-DURATION TO BAD-DURATION
MOVE IN-START-TIME TO BAD-START-TIME
MOVE IN-START-DATE TO BAD-START-DATE
MOVE IN-RECORD-ID TO BAD-RECORD-ID
IF WS-REC-ERR-COUNT > 0
MOVE WS-RE-CODE(1) TO BAD-ERR-CODE
MOVE WS-RE-SEVERITY(1) TO BAD-SEVERITY
ELSE
MOVE 'E001' TO BAD-ERR-CODE
MOVE 'HIGH' TO BAD-SEVERITY END-IF
WRITE BAD-REC
IF WS-FILE-BAD-STATUS NOT = '00'
DISPLAY '3300: WRITE BAD failed status='
WS-FILE-BAD-STATUS END-IF
DISPLAY '3300: Wrote BAD #' WS-TOTAL-READ
' code=' BAD-ERR-CODE END-IF.
*> Write each error detail to ERROR-REPORT
PERFORM VARYING WS-REC-ERR-IDX FROM 1 BY 1
UNTIL WS-REC-ERR-IDX > WS-REC-ERR-COUNT
MOVE IN-RECORD-ID TO ERR-RPT-RECORD-ID
MOVE WS-RE-FIELD(WS-REC-ERR-IDX)
TO ERR-RPT-FIELD-NAME
MOVE WS-RE-CODE(WS-REC-ERR-IDX)
TO ERR-RPT-ERR-CODE
MOVE WS-RE-DESC(WS-REC-ERR-IDX)
TO ERR-RPT-ERR-DESC
MOVE WS-RE-SEVERITY(WS-REC-ERR-IDX)
TO ERR-RPT-SEVERITY
WRITE ERR-RPT-REC
IF WS-ERR-RPT-STATUS NOT = '00'
DISPLAY '3300: WRITE ERR-RPT failed status='
WS-ERR-RPT-STATUS END-IF
END-PERFORM.
EXIT SECTION.
*>
*> ============================================================
*> 4000-REPORT : Finalize error report with summary counts
*> ============================================================
4000-REPORT SECTION.
I4000-START.
DISPLAY 'ValidationNodup: 4000-REPORT...'.
MOVE WS-TOTAL-READ TO WS-ED-TOTAL.
MOVE WS-GOOD-COUNT TO WS-ED-GOOD.
MOVE WS-BAD-COUNT TO WS-ED-BAD.
MOVE WS-VALID-COUNT TO WS-ED-VALID.
MOVE WS-INVALID-COUNT TO WS-ED-INVALID.
MOVE WS-HASH-DURATION TO WS-ED-HASH.
*> Summary header
MOVE SPACES TO ERR-RPT-REC.
STRING '--- End of Error Detail ---'
INTO ERR-RPT-RECORD-ID.
WRITE ERR-RPT-REC.
MOVE SPACES TO ERR-RPT-REC.
STRING '*** ERROR REPORT SUMMARY ***'
INTO ERR-RPT-RECORD-ID.
WRITE ERR-RPT-REC.
MOVE SPACES TO ERR-RPT-REC.
STRING 'Batch: ' WS-TIMESTAMP
INTO ERR-RPT-RECORD-ID.
WRITE ERR-RPT-REC.
MOVE SPACES TO ERR-RPT-REC. WRITE ERR-RPT-REC.
*> Record counts
MOVE SPACES TO ERR-RPT-REC.
STRING 'Records read :' WS-ED-TOTAL
INTO ERR-RPT-RECORD-ID. WRITE ERR-RPT-REC.
MOVE SPACES TO ERR-RPT-REC.
STRING 'Records good :' WS-ED-GOOD
INTO ERR-RPT-RECORD-ID. WRITE ERR-RPT-REC.
MOVE SPACES TO ERR-RPT-REC.
STRING 'Records bad :' WS-ED-BAD
INTO ERR-RPT-RECORD-ID. WRITE ERR-RPT-REC.
MOVE SPACES TO ERR-RPT-REC.
STRING 'Valid (no errors) :' WS-ED-VALID
INTO ERR-RPT-RECORD-ID. WRITE ERR-RPT-REC.
MOVE SPACES TO ERR-RPT-REC.
STRING 'Invalid (w/errors):' WS-ED-INVALID
INTO ERR-RPT-RECORD-ID. WRITE ERR-RPT-REC.
MOVE WS-PROCESSED-COUNT TO WS-ED-TOTAL.
MOVE SPACES TO ERR-RPT-REC.
STRING 'Total processed :' WS-ED-TOTAL
INTO ERR-RPT-RECORD-ID. WRITE ERR-RPT-REC.
*> Hash totals
MOVE SPACES TO ERR-RPT-REC.
STRING 'Hash duration :' WS-ED-HASH
INTO ERR-RPT-RECORD-ID. WRITE ERR-RPT-REC.
MOVE SPACES TO ERR-RPT-REC. WRITE ERR-RPT-REC.
*> Error code breakdown
MOVE SPACES TO ERR-RPT-REC.
STRING '--- Error Code Breakdown ---'
INTO ERR-RPT-RECORD-ID. WRITE ERR-RPT-REC.
PERFORM VARYING WS-ED-IDX FROM 1 BY 1
UNTIL WS-ED-IDX > WS-ERR-DEF-COUNT
MOVE WS-ED-COUNT(WS-ED-IDX) TO WS-ED-ERR-COUNT
MOVE SPACES TO ERR-RPT-REC
STRING WS-ED-CODE(WS-ED-IDX) ' '
WS-ED-SEVERITY(WS-ED-IDX) ' '
WS-ED-ERR-COUNT ' '
WS-ED-DESC(WS-ED-IDX)(1:30)
INTO ERR-RPT-RECORD-ID
WRITE ERR-RPT-REC
END-PERFORM.
MOVE SPACES TO ERR-RPT-REC.
STRING '*** END OF REPORT ***'
INTO ERR-RPT-RECORD-ID. WRITE ERR-RPT-REC.
*> Close ERROR-REPORT
CLOSE ERROR-REPORT.
IF WS-ERR-RPT-STATUS NOT = '00' AND NOT = '10'
DISPLAY '4000: CLOSE ERROR-REPORT status='
WS-ERR-RPT-STATUS
END-IF.
DISPLAY 'ValidationNodup: 4000-REPORT complete'.
EXIT SECTION.
*>
*> ============================================================
*> 5000-AUDIT : Write audit summary with validation summary
*> ============================================================
5000-AUDIT SECTION.
I5000-START.
DISPLAY 'ValidationNodup: 5000-AUDIT...'.
MOVE FUNCTION CURRENT-DATE TO WS-CURRENT-TIME.
STRING WS-CURR-YEAR '-' WS-CURR-MONTH '-'
WS-CURR-DAY ' ' WS-CURR-HOUR ':'
WS-CURR-MIN ':' WS-CURR-SEC INTO WS-TIMESTAMP.
*> Audit header
MOVE SPACES TO AUDIT-REC.
STRING '=== VALIDATION AUDIT LOG ===' INTO AUDIT-REC.
WRITE AUDIT-REC.
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY '5000: WRITE AUDIT header status='
WS-AUDIT-STATUS END-IF.
*> Program ID and timestamp
MOVE SPACES TO AUDIT-REC.
STRING 'Program: ValidationNodup' INTO AUDIT-REC.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC.
STRING 'Batch timestamp: ' WS-TIMESTAMP INTO AUDIT-REC.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC. WRITE AUDIT-REC.
*> Summary counts
MOVE WS-TOTAL-READ TO WS-ED-TOTAL.
MOVE WS-GOOD-COUNT TO WS-ED-GOOD.
MOVE WS-BAD-COUNT TO WS-ED-BAD.
MOVE WS-VALID-COUNT TO WS-ED-VALID.
MOVE WS-INVALID-COUNT TO WS-ED-INVALID.
MOVE SPACES TO AUDIT-REC.
STRING 'Records read :' WS-ED-TOTAL INTO AUDIT-REC.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC.
STRING 'Records good :' WS-ED-GOOD INTO AUDIT-REC.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC.
STRING 'Records bad :' WS-ED-BAD INTO AUDIT-REC.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC.
STRING 'Valid (no err) :' WS-ED-VALID INTO AUDIT-REC.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC.
STRING 'Invalid (error):' WS-ED-INVALID INTO AUDIT-REC.
WRITE AUDIT-REC.
*> Control totals
MOVE SPACES TO AUDIT-REC.
STRING 'Control recs :' WS-CONTROL-TOTAL-REC
INTO AUDIT-REC. WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC.
STRING 'Control good :' WS-CONTROL-TOTAL-GOOD
INTO AUDIT-REC. WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC.
STRING 'Control bad :' WS-CONTROL-TOTAL-BAD
INTO AUDIT-REC. WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC. WRITE AUDIT-REC.
*> Hash totals (data integrity)
MOVE WS-HASH-DURATION TO WS-ED-HASH.
MOVE SPACES TO AUDIT-REC.
STRING 'Hash duration :' WS-ED-HASH INTO AUDIT-REC.
WRITE AUDIT-REC.
MOVE WS-HASH-CALLER-CHARS TO WS-ED-HASH.
MOVE SPACES TO AUDIT-REC.
STRING 'Hash caller :' WS-ED-HASH INTO AUDIT-REC.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC. WRITE AUDIT-REC.
*> Error code count breakdown
MOVE SPACES TO AUDIT-REC.
STRING '--- Error Counts by Code ---' INTO AUDIT-REC.
WRITE AUDIT-REC.
PERFORM VARYING WS-ED-IDX FROM 1 BY 1
UNTIL WS-ED-IDX > WS-ERR-DEF-COUNT
MOVE WS-ED-COUNT(WS-ED-IDX) TO WS-ED-ERR-COUNT
MOVE SPACES TO AUDIT-REC
STRING WS-ED-CODE(WS-ED-IDX) ' '
WS-ED-SEVERITY(WS-ED-IDX) ' '
WS-ED-ERR-COUNT ' '
WS-ED-DESC(WS-ED-IDX)(1:35)
INTO AUDIT-REC
WRITE AUDIT-REC
END-PERFORM.
MOVE SPACES TO AUDIT-REC. WRITE AUDIT-REC.
*> Severity legend and validation rules
MOVE SPACES TO AUDIT-REC.
STRING 'Severity: LOW=format MED=data HIGH=critical'
INTO AUDIT-REC. WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC.
STRING 'Rules: R1A/E001 R1B/E002 R2A/E003 R2B/E004'
INTO AUDIT-REC. WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC.
STRING ' R3A/E005 R3B/E006 R4A/E007 R4B/E008'
INTO AUDIT-REC. WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC. WRITE AUDIT-REC.
*> File list
MOVE SPACES TO AUDIT-REC.
STRING 'Input : file-in.dat' INTO AUDIT-REC. WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC.
STRING 'Output: file-out-good.dat' INTO AUDIT-REC.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC.
STRING 'Output: file-out-bad.dat' INTO AUDIT-REC.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC.
STRING 'Output: error-report.dat' INTO AUDIT-REC.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-REC.
STRING 'Output: audit-report.dat' INTO AUDIT-REC.
WRITE AUDIT-REC.
*> Audit footer
MOVE SPACES TO AUDIT-REC.
STRING '=== END AUDIT LOG ===' INTO AUDIT-REC.
WRITE AUDIT-REC.
*> Close AUDIT-FILE
CLOSE AUDIT-FILE.
IF WS-AUDIT-STATUS NOT = '00' AND NOT = '10'
DISPLAY '5000: CLOSE AUDIT-FILE status='
WS-AUDIT-STATUS END-IF.
DISPLAY 'ValidationNodup: 5000-AUDIT complete'.
EXIT SECTION.
*>
*> ============================================================
*> 6000-ERROR-HANDLE : Final error handler
*> ============================================================
6000-ERROR-HANDLE SECTION.
I6000-START.
DISPLAY 'ValidationNodup: 6000-ERROR-HANDLE...'.
IF WS-FILE-IN-STATUS NOT = '00' AND NOT = '10'
DISPLAY ' FILE-IN status=' WS-FILE-IN-STATUS END-IF.
IF WS-FILE-GOOD-STATUS NOT = '00' AND NOT = '10'
DISPLAY ' FILE-OUT-GOOD status=' WS-FILE-GOOD-STATUS
END-IF.
IF WS-FILE-BAD-STATUS NOT = '00' AND NOT = '10'
DISPLAY ' FILE-OUT-BAD status=' WS-FILE-BAD-STATUS
END-IF.
DISPLAY 'ValidationNodup: 6000 complete'.
EXIT SECTION.
*>
*> ============================================================
*> 9000-EXIT : Close files, display final summary, stop
*> ============================================================
9000-EXIT SECTION.
I9000-START.
DISPLAY 'ValidationNodup: 9000-EXIT...'.
CLOSE FILE-IN.
IF WS-FILE-IN-STATUS NOT = '00' AND NOT = '10'
DISPLAY ' CLOSE FILE-IN status=' WS-FILE-IN-STATUS
ELSE DISPLAY ' FILE-IN closed status='
WS-FILE-IN-STATUS END-IF.
CLOSE FILE-OUT-GOOD.
IF WS-FILE-GOOD-STATUS NOT = '00' AND NOT = '10'
DISPLAY ' CLOSE GOOD status=' WS-FILE-GOOD-STATUS
ELSE DISPLAY ' FILE-OUT-GOOD closed status='
WS-FILE-GOOD-STATUS END-IF.
CLOSE FILE-OUT-BAD.
IF WS-FILE-BAD-STATUS NOT = '00' AND NOT = '10'
DISPLAY ' CLOSE BAD status=' WS-FILE-BAD-STATUS
ELSE DISPLAY ' FILE-OUT-BAD closed status='
WS-FILE-BAD-STATUS END-IF.
*> Final timestamp
MOVE FUNCTION CURRENT-DATE TO WS-CURRENT-TIME.
STRING WS-CURR-YEAR '-' WS-CURR-MONTH '-'
WS-CURR-DAY ' ' WS-CURR-HOUR ':'
WS-CURR-MIN ':' WS-CURR-SEC INTO WS-TIMESTAMP.
*> Edited counters
MOVE WS-TOTAL-READ TO WS-ED-TOTAL.
MOVE WS-GOOD-COUNT TO WS-ED-GOOD.
MOVE WS-BAD-COUNT TO WS-ED-BAD.
MOVE WS-VALID-COUNT TO WS-ED-VALID.
MOVE WS-INVALID-COUNT TO WS-ED-INVALID.
MOVE WS-HASH-DURATION TO WS-ED-HASH.
*> Display final summary
DISPLAY ' '
DISPLAY '========================================'.
DISPLAY 'ValidationNodup: FINAL SUMMARY'.
DISPLAY 'End: ' WS-TIMESTAMP.
DISPLAY 'Records read : ' WS-ED-TOTAL.
DISPLAY 'Records good (valid): ' WS-ED-GOOD.
DISPLAY 'Records bad (invalid): ' WS-ED-BAD.
DISPLAY 'Valid (no errors) : ' WS-ED-VALID.
DISPLAY 'Invalid (w/errors) : ' WS-ED-INVALID.
DISPLAY 'Hash duration total : ' WS-ED-HASH.
DISPLAY 'Error breakdown:'
PERFORM VARYING WS-ED-IDX FROM 1 BY 1
UNTIL WS-ED-IDX > WS-ERR-DEF-COUNT
MOVE WS-ED-COUNT(WS-ED-IDX) TO WS-ED-ERR-COUNT
DISPLAY ' ' WS-ED-CODE(WS-ED-IDX) ' '
WS-ED-SEVERITY(WS-ED-IDX) ' '
WS-ED-ERR-COUNT ' '
WS-ED-DESC(WS-ED-IDX)(1:30)
END-PERFORM.
DISPLAY 'Error report : error-report.dat'.
DISPLAY 'Audit report : audit-report.dat'.
DISPLAY '========================================'.
MOVE WS-RETURN-CODE TO RETURN-CODE.
DISPLAY 'ValidationNodup: Done. RC=' WS-RETURN-CODE.
STOP RUN.
*>
END PROGRAM ValidationNodup.