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 textid for i18n-aware text rendering via GET_TEXT / MESSAGE … TYPE …
  • A numeric exc_no for 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 PREVIOUS exception when wrapping framework exceptions, preserving the full cause trail

2. Naming and location

ObjectName
Exception classZCX_HR_INFOTYPE_READER
SuperclassCX_STATIC_CHECK
Message classZHR_INFOTYPE_READER (new — see §7)
Packagesame 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.

AttributeTypePurpose
if_t100_message~t100keySCX_T100KEYIdentifies the text-ID variant of the exception (see §5). Carries MSGID + MSGNO directly; used by GET_TEXT( ).
previousREF TO CX_ROOT (inherited)Wrapped cause, typically CX_HRPA_VIOLATED_ASSERTION
exc_noINumeric exception code (see §6)
pernrPERSNOPersonnel number in context, or initial
inftyINFTYInfotype in context, or initial
subtySUBTYSubtype in context, or initial
msgtySYMSGTYTypically 'E'
msgidSYMSGIDMessage class ID (see §7)
msgnoSYMSGNOMessage number within the class
msgv1..msgv4SYMSGVMessage 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 nameMSGNOPlain-language meaning
INVALID_PERNR001PERNR has no PA0001 record overlapping the input window
INVALID_DATE_RANGE002BEGDA > ENDDA or either is initial
NO_INFOTYPES_REQUESTED003IT_INFOTYPES is empty
STRUCTURE_MISMATCH004Caller structure incompatible with requested IT_INFOTYPES
READ_FAILURE005Wrapped CX_HRPA_VIOLATED_ASSERTION from READ / READ_WIDE
CONVERSION_FAILURE006CL_HR_PNNNN_TYPE_CAST=>PRELP_TO_PNNNN_TAB failed
MANDATORY_INFOTYPE_EMPTY007Infotype flagged as mandatory returned no records
BUFFER_ERROR008Inconsistency detected in the instance PRELP cache
READER_NOT_AVAILABLE010CL_HRPA_READ_INFOTYPE=>GET_INSTANCE failed
INTERNAL_ERROR099Catch-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.

MSGNOMSGTEXT (long text optional)
001No PA0001 record found for PERNR &1 in period &2 - &3
002Invalid date range: BEGDA &1 must be <= ENDDA &2 and both must be filled
003No infotypes were requested — IT_INFOTYPES is empty
004Caller structure is incompatible with requested infotypes (missing &1)
005Framework read failed for PERNR &1 infotype &2
006PRELP-to-PNNNN conversion failed for PERNR &1 infotype &2
007Mandatory infotype &1 returned no records for PERNR &2 in period &3 - &4
008Inconsistent buffer state for PERNR &1 infotype &2
010CL_HRPA_READ_INFOTYPE instance could not be obtained
099Internal 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_WARNING helper emits every warning it produces (MISSING_AUTH, orphan secondary, unknown T777D-INFKN, extra structure component) with MSGNO = '010', which collides with the READER_NOT_AVAILABLE entry 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 the pernr/infty fields of TY_MESSAGE and on the msgv* payload is reliable; rendering via MESSAGE ID ... NUMBER ... is not. Tracked in infotype_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):

  1. Call super->constructor( previous = previous ). Do not pass textid to the super call — the inherited CX_ROOT~TEXTID is SOTR_CONC (C(32)) and is type-incompatible with SCX_T100KEY.
  2. Populate IF_T100_MESSAGE~T100KEY:
    • if the caller's textid is initial, use if_t100_message=>default_textid;
    • otherwise, assign the caller's textid to if_t100_message~t100key.
  3. Assign the remaining passed values (exc_no, pernr, infty, subty, msgv1..msgv4) to the corresponding READ-ONLY attributes.
  4. 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:

  1. textid — always set, one of the declared constants.
  2. exc_no — always set, matching the textid.
  3. pernr / infty / subty — set when the condition is per-employee / per-infotype. Caller-side code frequently wants these without parsing the message text.
  4. msgv1..msgv4 — set so that the resulting MESSAGE lx TYPE 'E' renders readable text with the message-class template. Follow the placeholder mapping in §7.
  5. 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

#ScenarioExpected
1Raise with textid = READ_FAILURE, previous = lx_rootlx->textid, lx->exc_no = 5, lx->previous IS BOUND
2Raise INVALID_PERNR, get_text( )Rendered text contains PERNR, BEGDA, ENDDA placeholders
3All ten textids each produce distinct exc_noNo collisions
4Catch as CX_STATIC_CHECKCatches
5Catch as CX_ROOTCatches (for catch-all frames)
6Re-raise after catchprevious chain preserved two levels deep

12. Open questions

  1. Translation / message maintenance — the message class ZHR_INFOTYPE_READER is 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.
  2. exc_no gaps — values 9 and 11–98 are unassigned. Reserve them or not? Default recommendation: leave reserved without comment, allocate sequentially as new conditions appear.
  3. Dedicated warning MSGNOs — see §7 "Known issue". Allocate a block (proposal: 901–909) for warnings emitted by the reader's APPEND_WARNING and stop reusing 010.
Built with LogoFlowershow