RAP Basics
Architect / Cross-trackadvanced

RAP Basics Part 9: Behavior Implementation — Handler and Saver Classes

Key takeaway

Implement RAP business logic in the behavior pool: the ZBP global class, local handler classes (cl abap behavior handler) with FOR VALIDATE, FOR DETERMINE, FOR MODIFY and FOR INSTANCE FEATURES methods, the saver class (cl abap behavior saver), derived types, %tky, failed and reported, and IN LOCAL MODE.

Explanation

📌 KEY POINTS
✅ The behavior pool is a global class (ZBP_I_TRAVEL) that is empty by itself; the logic is in local classes in the "Local Types" tab.

✅ Handler class (lhc_travel, inherits cl_abap_behavior_handler) holds validations, determinations, actions, features and authorization methods.

✅ Saver class (lsc_zi_travel, inherits cl_abap_behavior_saver) holds the save sequence methods: finalize, check_before_save, save/save_modified, cleanup.

✅ Parameters are derived types: keys, result, failed, reported, mapped; %tky is the transactional key (includes %is_draft).

⚠️ Always use IN LOCAL MODE inside the BO — otherwise your own logic is blocked by feature control and authorization meant for consumers.

━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE

Part 7 declared validations, determinations and actions. Now we implement them.

━━━━━━━━━━━━━━━━━━━━
▶ 1. THE BEHAVIOR POOL

  • Created from the BDEF quick fix; global class has no code.
  • Local Types: one handler class per entity, one saver class for the BO.
  • Method signatures are generated — do not type them by hand.

━━━━━━━━━━━━━━━━━━━━
▶ 2. HANDLER METHOD KINDS

  • FOR VALIDATE ON SAVE: validation; fill failed and reported.
  • FOR DETERMINE ON MODIFY / ON SAVE: determination; MODIFY ENTITIES IN LOCAL MODE.
  • FOR MODIFY … FOR ACTION: action; return result.
  • FOR INSTANCE FEATURES / FOR INSTANCE AUTHORIZATION.\n\n━━━━━━━━━━━━━━━━━━━━\n▶ 3. DERIVED TYPES AND %-COMPONENTS\n• TABLE FOR READ RESULT zi_travel\travel, TABLE FOR UPDATE …
  • %tky: transactional key; %key: key only; %control: which fields are set; %msg: message; %element: field for message highlighting.

━━━━━━━━━━━━━━━━━━━━
▶ 4. FAILED AND REPORTED

  • failed-travel: append %tky → the instance failed.
  • reported-travel: append %tky, %msg (new_message or message class), %element-CustomerID = if_abap_behv=>mk-on.
  • In draft, add a %state_area to clear old messages.

━━━━━━━━━━━━━━━━━━━━
▶ 5. SAVER CLASS

  • Managed: redefine only if additional/unmanaged save.
  • Unmanaged: implement finalize, check_before_save, save, cleanup.
  • Never COMMIT WORK — the framework commits.

━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP

  1. Generate the behavior pool from the BDEF.
  2. Use quick fix to create all declared methods.
  3. Implement validateCustomer: read, check with SELECT on customer, fill failed/reported.
  4. Implement setInitialStatus with MODIFY ENTITIES IN LOCAL MODE.
  5. Implement acceptTravel and return result $self.

✅ CHECKPOINT: saving a travel with an unknown customer shows the error on the Customer field and nothing is saved.

Code example

ABAP Code
* ===== STEP 1: handler class definition (Local Types of ZBP_I_TRAVEL) =====CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler.  PRIVATE SECTION.    METHODS validatecustomer FOR VALIDATE ON SAVE      IMPORTING keys FOR Travel~validateCustomer.    METHODS setinitialstatus FOR DETERMINE ON MODIFY      IMPORTING keys FOR Travel~setInitialStatus.    METHODS accepttravel FOR MODIFY      IMPORTING keys FOR ACTION Travel~acceptTravel RESULT result.ENDCLASS. CLASS lhc_travel IMPLEMENTATION. * ===== STEP 2: validation - check, never modify =====  METHOD validatecustomer.    DATA lt_travel   TYPE TABLE FOR READ RESULT zi_travel\travel.    DATA ls_travel   LIKE LINE OF lt_travel.    DATA lv_customer TYPE /dmo/customer_id.    DATA ls_failed   LIKE LINE OF failed-travel.    DATA ls_reported LIKE LINE OF reported-travel.     READ ENTITIES OF zi_travel IN LOCAL MODE      ENTITY Travel FIELDS ( CustomerID ) WITH CORRESPONDING #( keys )      RESULT lt_travel.     LOOP AT lt_travel INTO ls_travel.      CLEAR lv_customer.      SELECT SINGLE customer_id FROM /dmo/customer        INTO lv_customer        WHERE customer_id = ls_travel-CustomerID.      IF sy-subrc <> 0.        CLEAR ls_failed.        ls_failed-%tky = ls_travel-%tky.        APPEND ls_failed TO failed-travel.         CLEAR ls_reported.        ls_reported-%tky = ls_travel-%tky.        ls_reported-%msg = new_message_with_text(                             severity = if_abap_behv_message=>severity-error                             text     = 'Customer does not exist' ).        ls_reported-%element-CustomerID = if_abap_behv=>mk-on.        APPEND ls_reported TO reported-travel.      ENDIF.    ENDLOOP.  ENDMETHOD. * ===== STEP 3: determination - derive status on create =====  METHOD setinitialstatus.    MODIFY ENTITIES OF zi_travel IN LOCAL MODE      ENTITY Travel UPDATE FIELDS ( OverallStatus )      WITH VALUE #( FOR ls_key IN keys ( %tky = ls_key-%tky OverallStatus = 'O' ) ).  ENDMETHOD. * ===== STEP 4: action - set status and return the instance =====  METHOD accepttravel.    DATA lt_travel TYPE TABLE FOR READ RESULT zi_travel\travel.    DATA ls_travel LIKE LINE OF lt_travel.    DATA ls_result LIKE LINE OF result.     MODIFY ENTITIES OF zi_travel IN LOCAL MODE      ENTITY Travel UPDATE FIELDS ( OverallStatus )      WITH VALUE #( FOR ls_key IN keys ( %tky = ls_key-%tky OverallStatus = 'A' ) ).     READ ENTITIES OF zi_travel IN LOCAL MODE      ENTITY Travel ALL FIELDS WITH CORRESPONDING #( keys )      RESULT lt_travel.     LOOP AT lt_travel INTO ls_travel.      CLEAR ls_result.      ls_result-%tky   = ls_travel-%tky.      ls_result-%param = ls_travel.      APPEND ls_result TO result.    ENDLOOP.  ENDMETHOD.ENDCLASS.

Real project scenario

In a travel-expense BO, the team implemented acceptTravel without IN LOCAL MODE. The action worked in unit tests with a developer user but failed for approvers in quality: the status field was readonly for consumers and the approver lacked the edit authorization, so the framework rejected the action's own UPDATE. Adding IN LOCAL MODE to the MODIFY and READ statements inside the behavior pool fixed it, because internal BO logic bypasses consumer-level feature control and authorization checks. In the same review, a validation that set a default currency was split: the check stayed as a validation, the default moved into a determination.

Common mistakes

  • Missing IN LOCAL MODE in behavior pool EML, so internal updates are blocked by feature control or authorization.
  • Changing data inside a validation.
  • Filling reported without failed (message shown but save continues) or failed without reported (save blocked without explanation).
  • Typing method signatures by hand instead of using the quick fix.
  • Using COMMIT WORK or CALL FUNCTION IN UPDATE TASK directly in handler methods.

Best practices

  • Generate methods via quick fix; keep one handler class per entity.
  • Always fill both failed and reported for rejected instances, with %element for field highlighting.
  • Use IN LOCAL MODE for all EML inside the BO.
  • Read only the fields you need (FIELDS ( … )), in one READ ENTITIES per method — no reads in loops.
  • Put select logic for checks in bulk (FOR ALL ENTRIES or a range) when many keys arrive.

Interview angle

Questions: "What is a behavior pool?", "What are failed and reported?", "Why IN LOCAL MODE?"
Strong answer: the behavior pool is the ZBP_* class whose local handler classes implement validations, determinations, actions, features and authorization, and whose saver class implements the save sequence. failed marks instances that failed; reported carries messages with %element for field highlighting. IN LOCAL MODE lets internal BO logic bypass consumer feature control and authorization. Senior answers mention %tky including %is_draft and bulk processing of keys.

Practice this lesson

Quick check

1. A validation finds that one of five travels has an invalid customer. What should the method do for that travel?

2. Where are validation, determination and action methods of a RAP BO implemented?

3. Why should EML statements inside a behavior pool use IN LOCAL MODE?

Interview questions

1. An approve action for expense claims reads each selected claim with a separate SELECT SINGLE in a loop and raises a message when one fails, but the other claims still get approved without any explanation for the failed one. How do you improve the implementation?

Answer it in your own words first, then compare.

2. An acceptTravel action works in development but in quality approvers get an error that the status field cannot be changed or that they are not authorized — even though they are allowed to approve. The action updates OverallStatus with MODIFY ENTITIES. What is the likely cause?

Answer it in your own words first, then compare.

Practice more with Interview AI →

Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.

More lessons in this topic

Interview questions on this topic

Related SAP topics