Specification — ZCXHRINFOTYPEREADER
Specification — ZCX_HR_INFOTYPE_READER
Status: Draft v1.0
Related: ../CLAUDE.md, infotype_reader_spec.md, infotype_reading_pattern.md
1. Purpose
Single typed exception class for all hard errors raised by ZCL_HR_INFOTYPE_READER. Carries:
- A message-class
textidfor i18n-aware text rendering viaGET_TEXT/MESSAGE … TYPE … - A numeric
exc_nofor programmatic dispatch by callers (the "exception number" required by the functional spec) - BAPI-style message fields (
MSGTY/MSGID/MSGNO/MSGV1..4) for propagation into application logs, BAPIRET2 structures, or UI messages - Contextual attributes (
PERNR,INFTY,SUBTY) so the catching code can produce a useful error message without re-deriving context - The chained
PREVIOUSexception when wrapping framework exceptions, preserving the full cause trail
2. Naming and location
| Object | Name |
|---|---|
| Exception class | ZCX_HR_INFOTYPE_READER |
| Superclass | CX_STATIC_CHECK |
| Message class | ZHR_INFOTYPE_READER (new — see §7) |
| Package | same as ZCL_HR_INFOTYPE_READER |
3. Inheritance
CX_STATIC_CHECK, not CX_DYNAMIC_CHECK or CX_NO_CHECK.
Rationale: every place that calls READ_AGGREGATED_* is expected to handle the exception explicitly via TRY .. CATCH. Static check forces the compiler to flag missing handlers, which is what we want for a utility class that is likely to be called from many unrelated programs over time. Dynamic check would allow callers to silently let the exception propagate through layers that don't know how to handle it.
4. Attributes
The class implements IF_T100_MESSAGE, which gives it the public attribute IF_T100_MESSAGE~T100KEY (type SCX_T100KEY) used by GET_TEXT( ) to render text from the message class. All other attributes below are READ-ONLY public instance attributes set in the constructor.
| Attribute | Type | Purpose |
|---|---|---|
if_t100_message~t100key | SCX_T100KEY | Identifies the text-ID variant of the exception (see §5). Carries MSGID + MSGNO directly; used by GET_TEXT( ). |
previous | REF TO CX_ROOT (inherited) | Wrapped cause, typically CX_HRPA_VIOLATED_ASSERTION |
exc_no | I | Numeric exception code (see §6) |
pernr | PERSNO | Personnel number in context, or initial |
infty | INFTY | Infotype in context, or initial |
subty | SUBTY | Subtype in context, or initial |
msgty | SYMSGTY | Typically 'E' |
msgid | SYMSGID | Message class ID (see §7) |
msgno | SYMSGNO | Message number within the class |
msgv1..msgv4 | SYMSGV | Message placeholders; populated from context |
The inherited CX_ROOT~TEXTID (SOTR_CONC) attribute is left initial — it is the legacy SOTR text-ID slot and is not used by this class. Dispatch on IF_T100_MESSAGE~T100KEY or EXC_NO instead.
5. Text IDs
Declare each as a public structured CONSTANTS block with the shape of SCX_T100KEY (msgid, msgno, attr1..attr4). msgid is always 'ZHR_INFOTYPE_READER'; msgno is the value from the table below; attr1..attr4 stay empty (placeholder binding happens via msgv1..msgv4, not via structured attribute names). Use short, descriptive names (upper-snake). All messages live in message class ZHR_INFOTYPE_READER (see §7).
Example:
CONSTANTS:
BEGIN OF invalid_pernr,
msgid TYPE symsgid VALUE 'ZHR_INFOTYPE_READER',
msgno TYPE symsgno VALUE '001',
attr1 TYPE scx_attrname VALUE '',
attr2 TYPE scx_attrname VALUE '',
attr3 TYPE scx_attrname VALUE '',
attr4 TYPE scx_attrname VALUE '',
END OF invalid_pernr,
...
textid name | MSGNO | Plain-language meaning |
|---|---|---|
INVALID_PERNR | 001 | PERNR has no PA0001 record overlapping the input window |
INVALID_DATE_RANGE | 002 | BEGDA > ENDDA or either is initial |
NO_INFOTYPES_REQUESTED | 003 | IT_INFOTYPES is empty |
STRUCTURE_MISMATCH | 004 | Caller structure incompatible with requested IT_INFOTYPES |
READ_FAILURE | 005 | Wrapped CX_HRPA_VIOLATED_ASSERTION from READ / READ_WIDE |
CONVERSION_FAILURE | 006 | CL_HR_PNNNN_TYPE_CAST=>PRELP_TO_PNNNN_TAB failed |
MANDATORY_INFOTYPE_EMPTY | 007 | Infotype flagged as mandatory returned no records |
BUFFER_ERROR | 008 | Inconsistency detected in the instance PRELP cache |
READER_NOT_AVAILABLE | 010 | CL_HRPA_READ_INFOTYPE=>GET_INSTANCE failed |
INTERNAL_ERROR | 099 | Catch-all for conditions not otherwise classified |
Number 009 is intentionally left unused to keep a gap before the framework-level READER_NOT_AVAILABLE — it can be used for a future per-call framework issue.
6. Numeric exception codes
Exposed as a public constants structure so callers can branch on it without string comparison:
CONSTANTS:
BEGIN OF c_exc_no,
invalid_pernr TYPE i VALUE 1,
invalid_date_range TYPE i VALUE 2,
no_infotypes_requested TYPE i VALUE 3,
structure_mismatch TYPE i VALUE 4,
read_failure TYPE i VALUE 5,
conversion_failure TYPE i VALUE 6,
mandatory_infotype_empty TYPE i VALUE 7,
buffer_error TYPE i VALUE 8,
reader_not_available TYPE i VALUE 10,
internal_error TYPE i VALUE 99,
END OF c_exc_no.
The numeric value maps 1:1 to MSGNO (except READER_NOT_AVAILABLE and INTERNAL_ERROR where the gaps are deliberate). This gives two equivalent dispatch modes:
" Mode A — branch on textid (structure comparison)
CATCH zcx_hr_infotype_reader INTO DATA(lx).
IF lx->if_t100_message~t100key = zcx_hr_infotype_reader=>structure_mismatch.
...
ENDIF.
" Mode B — branch on numeric code
CATCH zcx_hr_infotype_reader INTO DATA(lx).
CASE lx->exc_no.
WHEN zcx_hr_infotype_reader=>c_exc_no-structure_mismatch.
...
WHEN zcx_hr_infotype_reader=>c_exc_no-mandatory_infotype_empty.
...
ENDCASE.
Both modes are supported. exc_no is preferred for programmatic dispatch because it is a compact integer comparison; if_t100_message~t100key is preferred when the catching code is going to render the text anyway.
7. Message class ZHR_INFOTYPE_READER
Create message class ZHR_INFOTYPE_READER with the following entries. All MSGTY = 'E' by default at raise time.
MSGNO | MSGTEXT (long text optional) |
|---|---|
| 001 | No PA0001 record found for PERNR &1 in period &2 - &3 |
| 002 | Invalid date range: BEGDA &1 must be <= ENDDA &2 and both must be filled |
| 003 | No infotypes were requested — IT_INFOTYPES is empty |
| 004 | Caller structure is incompatible with requested infotypes (missing &1) |
| 005 | Framework read failed for PERNR &1 infotype &2 |
| 006 | PRELP-to-PNNNN conversion failed for PERNR &1 infotype &2 |
| 007 | Mandatory infotype &1 returned no records for PERNR &2 in period &3 - &4 |
| 008 | Inconsistent buffer state for PERNR &1 infotype &2 |
| 010 | CL_HRPA_READ_INFOTYPE instance could not be obtained |
| 099 | Internal error in ZCL_HR_INFOTYPE_READER: &1 |
Placeholders align with the attributes populated in the constructor — see §9 for the mapping.
Known issue — MSGNO 010 is currently reused for runtime warnings.
The reader's
APPEND_WARNINGhelper emits every warning it produces (MISSING_AUTH, orphan secondary, unknownT777D-INFKN, extra structure component) withMSGNO = '010', which collides with theREADER_NOT_AVAILABLEentry in the table above. Until dedicated warning numbers are allocated, callers that render warning messages from the message class will see the wrong text. Dispatching on thepernr/inftyfields ofTY_MESSAGEand on themsgv*payload is reliable; rendering viaMESSAGE ID ... NUMBER ...is not. Tracked ininfotype_reader_spec.md§14, open question 5.
8. Constructor
The constructor must cover both shapes that callers need: the classic textid+previous shape and the extended shape that also supplies exc_no, pernr, infty, subty, and explicit msgv1..msgv4.
METHODS constructor
IMPORTING
textid LIKE if_t100_message=>t100key OPTIONAL
previous LIKE previous OPTIONAL
exc_no TYPE i OPTIONAL
pernr TYPE persno OPTIONAL
infty TYPE infty OPTIONAL
subty TYPE subty OPTIONAL
msgv1 TYPE symsgv OPTIONAL
msgv2 TYPE symsgv OPTIONAL
msgv3 TYPE symsgv OPTIONAL
msgv4 TYPE symsgv OPTIONAL.
Implementation (as realized in ZCX_HR_INFOTYPE_READER):
- Call
super->constructor( previous = previous ). Do not passtextidto the super call — the inheritedCX_ROOT~TEXTIDisSOTR_CONC(C(32)) and is type-incompatible withSCX_T100KEY. - Populate
IF_T100_MESSAGE~T100KEY:- if the caller's
textidis initial, useif_t100_message=>default_textid; - otherwise, assign the caller's
textidtoif_t100_message~t100key.
- if the caller's
- Assign the remaining passed values (
exc_no,pernr,infty,subty,msgv1..msgv4) to the correspondingREAD-ONLYattributes. - Fixed defaults applied unconditionally (no auto-context logic):
msgty='E'.msgid=if_t100_message~t100key-msgid.msgno=if_t100_message~t100key-msgno.
There is no auto-population of msgv1..msgv4 from pernr/infty/subty. The raise site is expected to pass the placeholders explicitly (e.g. msgv1 = CONV #( iv_pernr )). The attributes pernr/infty/subty exist purely for programmatic access by the catcher.
9. Raising — conventions
Every raise site follows this shape:
RAISE EXCEPTION TYPE zcx_hr_infotype_reader
EXPORTING
textid = zcx_hr_infotype_reader=>read_failure
exc_no = zcx_hr_infotype_reader=>c_exc_no-read_failure
pernr = iv_pernr
infty = iv_infty
msgv1 = CONV symsgv( iv_pernr )
msgv2 = CONV symsgv( iv_infty )
previous = lx_root.
Checklist at each raise site:
textid— always set, one of the declared constants.exc_no— always set, matching thetextid.pernr/infty/subty— set when the condition is per-employee / per-infotype. Caller-side code frequently wants these without parsing the message text.msgv1..msgv4— set so that the resultingMESSAGE lx TYPE 'E'renders readable text with the message-class template. Follow the placeholder mapping in §7.previous— set whenever wrapping a framework exception (CX_HRPA_VIOLATED_ASSERTION, etc.) so the root cause survives.
10. Text rendering
The inherited GET_TEXT( ) method returns the formatted message-class text with placeholders filled from MSGV1..MSGV4. Callers can use it directly:
CATCH zcx_hr_infotype_reader INTO DATA(lx).
MESSAGE lx TYPE 'E'. " renders via GET_TEXT
or
CATCH zcx_hr_infotype_reader INTO DATA(lx).
DATA(lv_text) = lx->get_text( ).
" log lv_text, lx->exc_no, lx->pernr, lx->infty ...
11. Test plan
| # | Scenario | Expected |
|---|---|---|
| 1 | Raise with textid = READ_FAILURE, previous = lx_root | lx->textid, lx->exc_no = 5, lx->previous IS BOUND |
| 2 | Raise INVALID_PERNR, get_text( ) | Rendered text contains PERNR, BEGDA, ENDDA placeholders |
| 3 | All ten textids each produce distinct exc_no | No collisions |
| 4 | Catch as CX_STATIC_CHECK | Catches |
| 5 | Catch as CX_ROOT | Catches (for catch-all frames) |
| 6 | Re-raise after catch | previous chain preserved two levels deep |
12. Open questions
- Translation / message maintenance — the message class
ZHR_INFOTYPE_READERis English-first. Ukrainian / Polish translations are out of scope for this class but should be filed as a follow-up in SE91 once texts stabilize. exc_nogaps — values 9 and 11–98 are unassigned. Reserve them or not? Default recommendation: leave reserved without comment, allocate sequentially as new conditions appear.- Dedicated warning MSGNOs — see §7 "Known issue". Allocate a block (proposal: 901–909) for warnings emitted by the reader's
APPEND_WARNINGand stop reusing010.