-%tky
+ %msg = new_message_with_text(
+ severity = if_abap_behv_message=>severity-error
+ text = 'Validation failed!' )
+ ) TO reported-root.
+ ENDIF.
+ ENDLOOP.
+
+ ENDMETHOD.
+
+ENDCLASS.
diff --git a/src/zbp_demo_abap_rap_ro_m.clas.xml b/src/zbp_demo_abap_rap_ro_m.clas.xml
new file mode 100644
index 0000000..919f460
--- /dev/null
+++ b/src/zbp_demo_abap_rap_ro_m.clas.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ ZBP_DEMO_ABAP_RAP_RO_M
+ E
+ Behavior implementation for RAP demo scenario (managed BO)
+ 06
+ 1
+ X
+ X
+ X
+ ZDEMO_ABAP_RAP_RO_M
+
+
+
+
diff --git a/src/zbp_demo_abap_rap_ro_u.clas.abap b/src/zbp_demo_abap_rap_ro_u.clas.abap
new file mode 100644
index 0000000..dc82b65
--- /dev/null
+++ b/src/zbp_demo_abap_rap_ro_u.clas.abap
@@ -0,0 +1,20 @@
+***********************************************************************
+*
+* RAP BO provider (i. e. ABAP behavior pool/ABP)
+* for a RAP demo scenario
+*
+* See more information in the CCIMP include (local types tab in ADT).
+*
+**********************************************************************
+"! Behavior implementation for RAP demo scenario (unmanaged BO)
+"! The class represents a RAP BO provider (i. e. an ABAP behavior pool/ABP) for a RAP demo scenario
+"! (unmanaged RAP BO with external numbering).
+CLASS zbp_demo_abap_rap_ro_u DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF zdemo_abap_rap_ro_u.
+ PROTECTED SECTION.
+ PRIVATE SECTION.
+ENDCLASS.
+
+
+
+CLASS ZBP_DEMO_ABAP_RAP_RO_U IMPLEMENTATION.
+ENDCLASS.
diff --git a/src/zbp_demo_abap_rap_ro_u.clas.locals_imp.abap b/src/zbp_demo_abap_rap_ro_u.clas.locals_imp.abap
new file mode 100644
index 0000000..a31144b
--- /dev/null
+++ b/src/zbp_demo_abap_rap_ro_u.clas.locals_imp.abap
@@ -0,0 +1,1031 @@
+***********************************************************************
+*
+* RAP BO provider (i. e. ABAP behavior pool/ABP)
+* for a RAP demo scenario
+*
+* - RAP scenario: unmanaged RAP BO, external numbering
+* - Data model: Consists of a root entity and one child entity. The BDEF
+* defines the behavior for these two entities which are connected via
+* a CDS composition relation. The definitions in the BDEF determine
+* which methods must be implemented in this ABAP behavior pool (ABP).
+*
+* ----------------------------- NOTE -----------------------------------
+* This simplified example is not a real life scenario and rather
+* focuses on the technical side by giving an idea how the communication
+* and data exchange between a RAP BO consumer, which is a class
+* in this case, and RAP BO provider can work. Additionally, it shows
+* how the methods for non-standard RAP BO operations might be
+* self-implemented in an ABP. The example focuses on specific RAP aspects.
+* For this reason, the example might not fully meet the requirements of
+* the RAP BO contract. Although this unmanaged scenario attempts to
+* replicate some of the functionality of the managed example, the
+* functionality is not fully consistent with the managed scenario.
+*
+* For demonstration purposes, some of the operations are
+* impacted by feature controls and instance authorization as specified
+* in the BDEF.
+*
+* The code presented in this class is intended only to support the ABAP
+* cheat sheets. It is not intended for direct use in a production system
+* environment. The code examples in the ABAP cheat sheets are primarily
+* intended to provide a better explanation and visualization of the
+* syntax and semantics of ABAP statements, not to solve concrete
+* programming tasks. For production application programs, you should
+* always work out your own solution for each individual case. There is
+* no guarantee for the correctness or completeness of the code.
+* Furthermore, there is no legal responsibility or liability for any
+* errors or their consequences that may occur when using the the example
+* code.
+*
+***********************************************************************
+
+***********************************************************************
+* Class lcl_buffer
+*
+* To have a self-contained and simple scenario, the transactional
+* buffer is realized by internal tables here. These tables - one for
+* the root entity, one for the child entity - are designed in a way
+* to include RAP BO instance data as well content IDs and flags to
+* specify if an instance is to be changed or deleted (which is relevant
+* for the save method in this example).
+*
+* The purpose of this class is to create these internal tables and
+* provide a method implementation for the preparation of the tables,
+* i. e. to prepare the content of the transactional buffer which is
+* accessed throughout the handler and saver method implementations.
+* The method/s is/are called in the context of each EML request.
+*
+***********************************************************************
+"Class that constitutes the transactional buffer
+CLASS lcl_buffer DEFINITION.
+ PUBLIC SECTION.
+
+ "Structure and internal table types for the internal table serving
+ "as transactional buffers for the root and child entities
+ TYPES: BEGIN OF gty_buffer,
+ instance TYPE zdemo_abap_rap_ro_u,
+ cid TYPE string,
+ changed TYPE abap_bool,
+ deleted TYPE abap_bool,
+ END OF gty_buffer.
+
+ TYPES: BEGIN OF gty_buffer_child,
+ instance TYPE zdemo_abap_rap_ch_u,
+ cid_ref TYPE string,
+ cid_target TYPE string,
+ changed TYPE abap_bool,
+ deleted TYPE abap_bool,
+ END OF gty_buffer_child.
+
+ TYPES gtt_buffer TYPE TABLE OF gty_buffer WITH EMPTY KEY.
+ TYPES gtt_buffer_child TYPE TABLE OF gty_buffer_child WITH EMPTY KEY.
+ "Structure and internal table types to include the keys for buffer preparation methods
+ TYPES: BEGIN OF root_keys,
+ key_field TYPE zdemo_abap_rap_ro_u-key_field,
+ END OF root_keys,
+ BEGIN OF child_keys,
+ key_field TYPE zdemo_abap_rap_ch_u-key_field,
+ key_ch TYPE zdemo_abap_rap_ch_u-key_ch,
+ full_key TYPE abap_bool,
+ END OF child_keys,
+ tt_root_keys TYPE TABLE OF root_keys WITH EMPTY KEY,
+ tt_child_keys TYPE TABLE OF child_keys WITH EMPTY KEY.
+
+ CLASS-DATA:
+ "Internal tables serving as transactional buffers for the root and child entities
+ root_buffer TYPE STANDARD TABLE OF gty_buffer WITH EMPTY KEY,
+ child_buffer TYPE STANDARD TABLE OF gty_buffer_child WITH EMPTY KEY.
+
+ "Buffer preparation methods
+ CLASS-METHODS: prep_root_buffer IMPORTING keys TYPE tt_root_keys,
+ prep_child_buffer IMPORTING keys TYPE tt_child_keys.
+
+ENDCLASS.
+
+
+CLASS lcl_buffer IMPLEMENTATION.
+
+ "Buffer preparation for the root entity based on the requested key values
+ METHOD prep_root_buffer.
+
+ LOOP AT keys ASSIGNING FIELD-SYMBOL().
+ "Logic:
+ "- Line with the specific key values exists in the buffer for the root entity
+ "- If it is true: Do nothing, buffer is prepared for the specific instance.
+ "- Note: If the line is marked as deleted, the buffer should not be filled anew with the data.
+ IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = -key_field ] ).
+ "Do nothing, buffer is prepared for the specific instance.
+ ELSE.
+ "Checking if entry exists in the database table of the root entity based on the key value
+ SELECT SINGLE @abap_true
+ FROM zdemo_abap_rapt1
+ WHERE key_field = @-key_field
+ INTO @DATA(exists).
+
+ IF exists = abap_true.
+ "If entry exists, retrieve it based on the shared key value
+ DATA line TYPE zdemo_abap_rap_ro_u.
+
+ SELECT SINGLE * FROM zdemo_abap_rapt1
+ WHERE key_field = @-key_field
+ INTO CORRESPONDING FIELDS OF @line.
+
+ IF sy-subrc = 0.
+ "Adding line to the root buffer
+ APPEND VALUE #( instance = line ) TO lcl_buffer=>root_buffer.
+ ENDIF.
+
+ ENDIF.
+ ENDIF.
+ ENDLOOP.
+ ENDMETHOD.
+
+ "Buffer preparation for the child entity based on the requested key values
+ METHOD prep_child_buffer.
+
+ LOOP AT keys ASSIGNING FIELD-SYMBOL().
+
+ "The full_key flag is in this example only relevant if a read operation is executed on the child entity directly
+ "and all key values should be considered for the data retrieval from the database table.
+ IF -full_key = abap_true.
+ "Logic:
+ "- Line with specific key values exists in the buffer for the child entity
+ "- If it is true: Do nothing, buffer is prepared for the specific instance.
+ IF line_exists( lcl_buffer=>child_buffer[ instance-key_field = -key_field
+ instance-key_ch = -key_ch ] ).
+ "Buffer is prepared for the instance.
+ ELSE.
+ "Checking if entry exists in the database table of the child entity based on the shared key value
+ SELECT SINGLE @abap_true
+ FROM zdemo_abap_rapt2
+ WHERE key_field = @-key_field
+ AND key_ch = @-key_ch
+ INTO @DATA(exists).
+
+ "If entry exists, retrieve all entries based on the key values
+ IF exists = abap_true.
+
+ DATA line_ch TYPE zdemo_abap_rap_ch_u.
+ SELECT SINGLE * FROM zdemo_abap_rapt2
+ WHERE key_field = @-key_field
+ AND key_ch = @-key_ch
+ INTO CORRESPONDING FIELDS OF @line_ch.
+
+ IF sy-subrc = 0.
+ "Adding line to the child buffer if no line exists with all key values
+ APPEND VALUE #( instance = line_ch ) TO lcl_buffer=>child_buffer.
+ ENDIF.
+
+ ENDIF.
+ ENDIF.
+
+ ELSE.
+
+ "Logic:
+ "- Line with specific keys exists in the buffer for the root entity and is marked for deletion
+ "- If all is true: Doing nothing, buffer is prepared for the specific instance.
+ "- Else: Retrieving all lines from the database table of the child entity having the shared key
+ IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = -key_field ] )
+ AND VALUE #( lcl_buffer=>root_buffer[ instance-key_field = -key_field ]-deleted OPTIONAL ) IS NOT INITIAL.
+ "Buffer is prepared for the instance.
+ ELSE.
+ "Checking if entry exists in the database table of the child entity based on the shared key value
+ SELECT SINGLE @abap_true
+ FROM zdemo_abap_rapt2
+ WHERE key_field = @-key_field
+ INTO @DATA(exists_ch).
+
+ "If entry exists, retrieve all entries based on the shared key value
+ IF exists_ch = abap_true.
+
+ DATA ch_tab TYPE TABLE OF zdemo_abap_rap_ch_u WITH EMPTY KEY.
+ SELECT * FROM zdemo_abap_rapt2
+ WHERE key_field = @-key_field
+ INTO CORRESPONDING FIELDS OF TABLE @ch_tab.
+
+ IF sy-subrc = 0.
+
+ LOOP AT ch_tab ASSIGNING FIELD-SYMBOL().
+ "Adding line to the child buffer if no line exists with all key values
+ IF NOT line_exists( lcl_buffer=>child_buffer[ instance-key_field = -key_field
+ instance-key_ch = -key_ch ] ).
+ APPEND VALUE #( instance = ) TO lcl_buffer=>child_buffer.
+ ENDIF.
+ ENDLOOP.
+ ENDIF.
+
+ ENDIF.
+ ENDIF.
+ ENDIF.
+ ENDLOOP.
+ ENDMETHOD.
+
+ENDCLASS.
+
+***********************************************************************
+* Local handler class lhc_root
+*
+* Contains handler method definitions and implementations as defined
+* in the CDS behavior definition (BDEF).
+*
+***********************************************************************
+
+CLASS lhc_root DEFINITION INHERITING FROM cl_abap_behavior_handler.
+
+ PRIVATE SECTION.
+
+ METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION
+ IMPORTING REQUEST requested_authorizations FOR root RESULT result.
+
+ METHODS create FOR MODIFY
+ IMPORTING entities FOR CREATE root.
+
+ METHODS read FOR READ
+ IMPORTING keys FOR READ root RESULT result.
+
+ METHODS lock FOR LOCK
+ IMPORTING keys FOR LOCK root.
+
+ METHODS update FOR MODIFY
+ IMPORTING entities FOR UPDATE root.
+
+ METHODS delete FOR MODIFY
+ IMPORTING keys FOR DELETE root.
+
+ METHODS rba_child FOR READ
+ IMPORTING keys_rba FOR READ root\_child FULL result_requested RESULT result LINK association_links.
+
+ METHODS cba_child FOR MODIFY
+ IMPORTING entities_cba FOR CREATE root\_child.
+
+ METHODS multiply_by_2 FOR MODIFY
+ IMPORTING keys FOR ACTION root~multiply_by_2.
+
+ METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
+ IMPORTING keys REQUEST requested_authorizations FOR root RESULT result.
+
+ METHODS get_instance_features FOR INSTANCE FEATURES
+ IMPORTING keys REQUEST requested_features FOR root RESULT result.
+
+ METHODS multiply_by_3 FOR MODIFY
+ IMPORTING keys FOR ACTION root~multiply_by_3.
+
+ METHODS get_global_features FOR GLOBAL FEATURES
+ IMPORTING REQUEST requested_features FOR root RESULT result.
+
+ METHODS set_z FOR MODIFY
+ IMPORTING keys FOR ACTION root~set_z.
+
+ENDCLASS.
+
+CLASS lhc_root IMPLEMENTATION.
+
+ METHOD get_global_authorizations.
+ "No implementation. All users are authorized.
+ ENDMETHOD.
+
+ METHOD create.
+ "Preparing the transactional buffer based on the input BDEF derived type.
+ lcl_buffer=>prep_root_buffer( CORRESPONDING #( entities ) ).
+
+ "Processing requested entities sequentially
+ LOOP AT entities ASSIGNING FIELD-SYMBOL().
+ "Logic:
+ "- Line with the specific key does not exist in the buffer for the root entity
+ "- Line with the specific key exists in the buffer but it is marked as deleted
+ "- If it is true: Add new instance to the buffer and, if needed, remove the instance marked as deleted beforehand
+ IF NOT line_exists( lcl_buffer=>root_buffer[ instance-key_field = -key_field ] )
+ OR line_exists( lcl_buffer=>root_buffer[ instance-key_field = -key_field deleted = abap_true ] ).
+
+ "If it exists, removing instance that is marked for deletion from the transactional buffer since it gets replaced by a new one.
+ DELETE lcl_buffer=>root_buffer WHERE instance-key_field = VALUE #(
+ lcl_buffer=>root_buffer[ instance-key_field = -key_field ]-instance-key_field OPTIONAL ) AND deleted = abap_true.
+
+ "Adding new instance to the transactional buffer by considering %control values
+ APPEND VALUE #( cid = -%cid
+ instance-key_field = -key_field
+ instance-field1 = COND #( WHEN -%control-field1 NE if_abap_behv=>mk-off
+ THEN -field1 )
+ instance-field2 = COND #( WHEN -%control-field2 NE if_abap_behv=>mk-off
+ THEN -field2 )
+ instance-field3 = COND #( WHEN -%control-field3 NE if_abap_behv=>mk-off
+ THEN -field3 )
+ instance-field4 = COND #( WHEN -%control-field4 NE if_abap_behv=>mk-off
+ THEN -field4 )
+ changed = abap_true
+ deleted = abap_false ) TO lcl_buffer=>root_buffer.
+
+ "Filling the MAPPED response parameter for the root entity
+ INSERT VALUE #( %cid = -%cid
+ %key = -%key ) INTO TABLE mapped-root.
+
+ ELSE.
+
+ "Filling FAILED and REPORTED response parameters
+ APPEND VALUE #( %cid = -%cid
+ %key = -%key
+ %create = if_abap_behv=>mk-on
+ %fail-cause = if_abap_behv=>cause-unspecific
+ ) TO failed-root.
+
+ APPEND VALUE #( %cid = -%cid
+ %key = -%key
+ %create = if_abap_behv=>mk-on
+ %msg = new_message_with_text(
+ severity = if_abap_behv_message=>severity-error
+ text = 'Create operation failed.' )
+ ) TO reported-root.
+
+ ENDIF.
+ ENDLOOP.
+ ENDMETHOD.
+
+ METHOD update.
+ "Preparing the transactional buffer based on the input BDEF derived type.
+ lcl_buffer=>prep_root_buffer( CORRESPONDING #( entities ) ).
+
+ "Processing requested entities sequentially
+ "Note:
+ "The example is implemented in a way that instances that failed in methods called before this method are not handled.
+ "The instances that have failed before this method call are not available in this method's input parameter.
+ "Hence, an adding to FAILED and REPORTED is not implemented here.
+ LOOP AT entities ASSIGNING FIELD-SYMBOL().
+
+ "Logic:
+ "- Line with the specific key exists in the buffer for the root entity and it is not marked as deleted
+ "- If it is true: Updating the buffer based on the input BDEF derived type and considering %control values
+ READ TABLE lcl_buffer=>root_buffer
+ WITH KEY instance-key_field = -key_field
+ deleted = abap_false
+ ASSIGNING FIELD-SYMBOL().
+
+ IF sy-subrc = 0.
+ -instance-field1 = COND #( WHEN -%control-field1 NE if_abap_behv=>mk-off
+ THEN -field1
+ ELSE -instance-field1 ).
+
+ -instance-field2 = COND #( WHEN -%control-field2 NE if_abap_behv=>mk-off
+ THEN -field2
+ ELSE -instance-field2 ).
+
+ -instance-field3 = COND #( WHEN -%control-field3 NE if_abap_behv=>mk-off
+ THEN -field3
+ ELSE -instance-field3 ).
+
+ -instance-field4 = COND #( WHEN -%control-field4 NE if_abap_behv=>mk-off
+ THEN -field4
+ ELSE -instance-field4 ).
+
+ -changed = abap_true.
+ -deleted = abap_false.
+ ENDIF.
+
+
+ ENDLOOP.
+ ENDMETHOD.
+
+ METHOD read.
+ "Preparing the transactional buffer based on the input BDEF derived type.
+ lcl_buffer=>prep_root_buffer( CORRESPONDING #( keys ) ).
+
+ "Processing requested keys sequentially
+ LOOP AT keys ASSIGNING FIELD-SYMBOL() GROUP BY -%tky.
+ "Logic:
+ "- Line exists in the buffer and it is not marked as deleted
+ "- If it is true: Adding the entries to the buffer based on the input BDEF derived type and considering %control values
+ READ TABLE lcl_buffer=>root_buffer
+ WITH KEY instance-key_field = -key_field
+ deleted = abap_false
+ ASSIGNING FIELD-SYMBOL().
+
+ IF sy-subrc = 0.
+
+ APPEND VALUE #( %tky = -%tky
+ field1 = COND #( WHEN -%control-field1 NE if_abap_behv=>mk-off
+ THEN -instance-field1 )
+ field2 = COND #( WHEN -%control-field2 NE if_abap_behv=>mk-off
+ THEN -instance-field2 )
+ field3 = COND #( WHEN -%control-field3 NE if_abap_behv=>mk-off
+ THEN -instance-field3 )
+ field4 = COND #( WHEN -%control-field4 NE if_abap_behv=>mk-off
+ THEN -instance-field4 )
+ ) TO result.
+
+ ELSE.
+
+ "Filling FAILED and REPORTED response parameters
+ APPEND VALUE #( %tky = -%tky
+ %fail-cause = if_abap_behv=>cause-not_found
+ ) TO failed-root.
+
+ APPEND VALUE #( %tky = -%tky
+ %msg = new_message_with_text(
+ severity = if_abap_behv_message=>severity-error
+ text = 'Read operation failed.' )
+ ) TO reported-root.
+
+ ENDIF.
+ ENDLOOP.
+ ENDMETHOD.
+
+ METHOD lock.
+
+ TRY.
+ "Instantiating lock object
+ DATA(lo_lock) = cl_abap_lock_object_factory=>get_instance(
+ iv_name = 'EZDEMO_ABAP_LOCK' ).
+
+ "Processing requested keys sequentially
+ LOOP AT keys REFERENCE INTO DATA(lr_key).
+ TRY.
+ lo_lock->enqueue( it_parameter = VALUE #(
+ ( name = 'KEY_FIELD' value = REF #( lr_key->key_field ) ) ) ).
+ CATCH cx_abap_foreign_lock.
+ APPEND VALUE #( %key = CORRESPONDING #( lr_key->* )
+ %fail-cause = if_abap_behv=>cause-locked )
+ TO failed-root.
+ CATCH cx_abap_lock_failure.
+ APPEND VALUE #( %key = CORRESPONDING #( lr_key->* )
+ %fail-cause = if_abap_behv=>cause-unspecific )
+ TO failed-root.
+ ENDTRY.
+ ENDLOOP.
+ CATCH cx_abap_lock_failure.
+ APPEND VALUE #( %fail-cause = if_abap_behv=>cause-unspecific )
+ TO failed-root.
+ ENDTRY.
+ ENDMETHOD.
+
+ METHOD delete.
+ "Preparing the transactional buffer based on the input BDEF derived type.
+ lcl_buffer=>prep_root_buffer( CORRESPONDING #( keys ) ).
+ "Preparing the child buffer to mark child instances when parent instances are marked for deletion.
+ lcl_buffer=>prep_child_buffer( VALUE #( FOR wa IN keys ( key_field = wa-key_field ) ) ).
+
+ "Processing requested keys sequentially
+ "Note:
+ "The example is implemented in a way that instances that failed in methods called before this method are not handled.
+ "The instances that have failed before this method call are not available in this method's input parameter.
+ "Hence, an adding to FAILED and REPORTED is not implemented here.
+ LOOP AT keys ASSIGNING FIELD-SYMBOL().
+ "Logic:
+ "- Line exists in the buffer and it is not marked as deleted
+ "- If it is true: Flagging the instance as deleted
+ READ TABLE lcl_buffer=>root_buffer
+ WITH KEY instance-key_field = -key_field
+ deleted = abap_false
+ ASSIGNING FIELD-SYMBOL().
+
+ IF sy-subrc = 0.
+ -changed = abap_false.
+ -deleted = abap_true.
+
+ "When parent instances are marked for deletion, child instances with the shared key should be marked as well.
+ LOOP AT lcl_buffer=>child_buffer ASSIGNING FIELD-SYMBOL() WHERE instance-key_field = -key_field.
+ -changed = abap_false.
+ -deleted = abap_true.
+ ENDLOOP.
+ ENDIF.
+ ENDLOOP.
+ ENDMETHOD.
+
+ METHOD rba_Child.
+ "Preparing the transactional buffers for both the root and child entity based on the input BDEF derived type.
+ lcl_buffer=>prep_root_buffer( CORRESPONDING #( keys_rba ) ).
+ lcl_buffer=>prep_child_buffer( CORRESPONDING #( keys_rba ) ).
+
+ "Processing requested keys sequentially
+ LOOP AT keys_rba ASSIGNING FIELD-SYMBOL() GROUP BY -key_field.
+ "Logic:
+ "- Line with the shared key value exists in the buffer for the root entity and it is not marked as deleted
+ "- Line with the shared key value exists in the child buffer
+ "- If it is true: Sequentially processing the child buffer entries (the example is set up in a way that there can be multiple entries)
+ IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = -key_field deleted = abap_false ] )
+ AND line_exists( lcl_buffer=>child_buffer[ instance-key_field = -key_field deleted = abap_false ] ).
+
+ LOOP AT lcl_buffer=>child_buffer ASSIGNING FIELD-SYMBOL() WHERE instance-key_field = -key_field.
+
+ "Filling the table for the LINK parameter
+ INSERT VALUE #( source-%tky = -%tky
+ target-%tky = VALUE #( key_field = -instance-key_field
+ key_ch = -instance-key_ch ) ) INTO TABLE association_links.
+
+ "Filling the table for the RESULT parameter based on the FULL parameter
+ "Note: If the FULL parameter is initial, only the LINK parameter should be provided
+ IF result_requested = abap_true.
+ APPEND VALUE #( key_field = -instance-key_field
+ key_ch = -instance-key_ch
+ field_ch1 = COND #( WHEN -%control-field_ch1 NE if_abap_behv=>mk-off
+ THEN -instance-field_ch1 )
+ field_ch2 = COND #( WHEN -%control-field_ch2 NE if_abap_behv=>mk-off
+ THEN -instance-field_ch2 )
+ ) TO result.
+ ENDIF.
+ ENDLOOP.
+
+ ELSE.
+
+ "Filling FAILED and REPORTED response parameters
+ APPEND VALUE #( %tky = -%tky
+ %fail-cause = if_abap_behv=>cause-not_found
+ %assoc-_child = if_abap_behv=>mk-on
+ ) TO failed-root.
+
+
+ APPEND VALUE #( %tky = -%tky
+ %msg = new_message_with_text(
+ severity = if_abap_behv_message=>severity-error
+ text = 'RBA (parent to child) operation failed.' )
+ ) TO reported-root.
+
+ ENDIF.
+ ENDLOOP.
+
+ "Removing potential duplicate entries
+ SORT association_links BY target ASCENDING.
+ DELETE ADJACENT DUPLICATES FROM association_links COMPARING ALL FIELDS.
+
+ SORT result BY %tky ASCENDING.
+ DELETE ADJACENT DUPLICATES FROM result COMPARING ALL FIELDS.
+
+ ENDMETHOD.
+
+ METHOD cba_Child.
+ "Preparing the transactional buffers for both the root and child entity based on the input BDEF derived type
+ lcl_buffer=>prep_root_buffer( CORRESPONDING #( entities_cba ) ).
+ lcl_buffer=>prep_child_buffer( CORRESPONDING #( entities_cba ) ).
+
+ "Processing requested entities sequentially
+ LOOP AT entities_cba ASSIGNING FIELD-SYMBOL() GROUP BY -key_field.
+ "Logic:
+ "- Line with the shared key value exists in the buffer for the root entity and it is not marked as deleted
+ "- If it is true: Sequentially processing the instances in the %target table
+ IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = -key_field deleted = abap_false ] ).
+
+ "If it exists, removing instance that is marked for deletion from the child transactional buffer since it gets replaced by a new one.
+ DELETE lcl_buffer=>child_buffer WHERE instance-key_field = -key_field AND deleted = abap_true.
+
+ LOOP AT -%target ASSIGNING FIELD-SYMBOL().
+
+ "Adding instance to child buffer if it does not exist there and considering %control values
+ "The example is implemented in a way that the RAP BO consumer need not specify the common key with the root entity.
+ "Plus, the keys of the child entity should not be initial.
+ IF NOT line_exists( lcl_buffer=>child_buffer[ instance-key_field = -key_field
+ instance-key_ch = -key_ch ] )
+ AND -key_ch IS NOT INITIAL.
+
+ APPEND VALUE #( cid_ref = -%cid_ref
+ cid_target = -%cid
+ instance-key_field = -key_field
+ instance-key_ch = -key_ch
+ instance-field_ch1 = COND #( WHEN -%control-field_ch1 NE if_abap_behv=>mk-off
+ THEN -field_ch1 )
+ instance-field_ch2 = COND #( WHEN -%control-field_ch2 NE if_abap_behv=>mk-off
+ THEN -field_ch2 )
+ changed = abap_true
+ ) TO lcl_buffer=>child_buffer.
+
+ "Filling MAPPED response parameter
+ INSERT VALUE #( %cid = -%cid
+ %key = VALUE #( key_field = -key_field
+ key_ch = -key_ch ) ) INTO TABLE mapped-child.
+
+ ELSE.
+
+ "Filling FAILED and REPORTED response parameters
+ APPEND VALUE #( %cid = -%cid_ref
+ %tky = -%tky
+ %assoc-_child = if_abap_behv=>mk-on
+ %fail-cause = if_abap_behv=>cause-unspecific
+ ) TO failed-root.
+
+ APPEND VALUE #( %cid = -%cid_ref
+ %tky = -%tky
+ %msg = new_message_with_text(
+ severity = if_abap_behv_message=>severity-error
+ text = 'Create-by-association (root to child) operation failed.' )
+ ) TO reported-root.
+
+ APPEND VALUE #( %cid = -%cid
+ %key = VALUE #( key_field = -key_field key_ch = -key_ch )
+ %fail-cause = if_abap_behv=>cause-dependency
+ ) TO failed-child.
+
+ APPEND VALUE #( %cid = -%cid
+ %key = VALUE #( key_field = -key_field key_ch = -key_ch )
+ %msg = new_message_with_text(
+ severity = if_abap_behv_message=>severity-error
+ text = 'Create-by-association (root to child) operation failed.' )
+ ) TO reported-child.
+
+ ENDIF.
+ ENDLOOP.
+
+ ELSE.
+
+ "Filling FAILED and REPORTED response parameters
+ APPEND VALUE #( %cid = -%cid_ref
+ %tky = -%tky
+ %assoc-_child = if_abap_behv=>mk-on
+ %fail-cause = if_abap_behv=>cause-not_found
+ ) TO failed-root.
+
+ APPEND VALUE #( %cid = -%cid_ref
+ %tky = -%tky
+ %msg = new_message_with_text(
+ severity = if_abap_behv_message=>severity-error
+ text = 'Create-by-association (root to child) operation failed.' )
+ ) TO reported-root.
+
+ LOOP AT -%target ASSIGNING FIELD-SYMBOL().
+ APPEND VALUE #( %cid = -%cid
+ %key = -%key
+ %fail-cause = if_abap_behv=>cause-dependency
+ ) TO failed-child.
+
+ APPEND VALUE #( %cid = -%cid
+ %key = -%key
+ %msg = new_message_with_text(
+ severity = if_abap_behv_message=>severity-error
+ text = 'Create-by-association (root to child) operation failed.' )
+ ) TO reported-child.
+ ENDLOOP.
+ ENDIF.
+ ENDLOOP.
+ ENDMETHOD.
+
+ METHOD multiply_by_2.
+
+ "Retrieving instances based on requested keys
+ READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
+ ENTITY root
+ FIELDS ( field3 field4 ) WITH CORRESPONDING #( keys )
+ RESULT DATA(result)
+ FAILED failed.
+
+ "If read result is initial, stop further method execution.
+ CHECK result IS NOT INITIAL.
+
+ "Setting %action value in failed response parameter
+ LOOP AT failed-root ASSIGNING FIELD-SYMBOL().
+ -%action-multiply_by_2 = if_abap_behv=>mk-on.
+ ENDLOOP.
+
+ "Multiply integer values by 2
+ MODIFY ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
+ ENTITY root
+ UPDATE FIELDS ( field3 field4 ) WITH VALUE #( FOR key IN result ( %tky = key-%tky
+ field3 = key-field3 * 2
+ field4 = key-field4 * 2 ) ).
+ ENDMETHOD.
+
+ METHOD get_instance_authorizations.
+
+ "Retrieving instances based on requested keys
+ READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
+ ENTITY root
+ FIELDS ( field1 ) WITH CORRESPONDING #( keys )
+ RESULT DATA(status)
+ FAILED failed.
+
+ "If the read result is initial, stop further method execution.
+ CHECK status IS NOT INITIAL.
+
+ LOOP AT status ASSIGNING FIELD-SYMBOL().
+
+ "If a specific field has a certain value, the deletion should be disallowed.
+ IF requested_authorizations-%delete = if_abap_behv=>mk-on.
+ APPEND VALUE #( %tky = -%tky
+ %op = VALUE #( %delete = COND #( WHEN -field1 = 'X'
+ THEN if_abap_behv=>auth-unauthorized
+ ELSE if_abap_behv=>auth-allowed ) )
+ ) TO result.
+
+ ENDIF.
+ ENDLOOP.
+
+ ENDMETHOD.
+
+ METHOD get_instance_features.
+
+ READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
+ ENTITY root
+ FIELDS ( field3 field4 ) WITH CORRESPONDING #( keys )
+ RESULT DATA(numbers)
+ FAILED failed.
+
+ "If the read result is initial, stop further method execution.
+ CHECK numbers IS NOT INITIAL.
+
+ LOOP AT numbers ASSIGNING FIELD-SYMBOL().
+
+ "If two fields have certain values, the execution of an action should be disabled for the instance.
+ IF requested_features-%action-multiply_by_3 = if_abap_behv=>mk-on.
+ APPEND VALUE #( %tky = -%tky
+ %features-%action-multiply_by_3 = COND #( WHEN -field3 = 0 OR -field4 = 0
+ THEN if_abap_behv=>fc-o-disabled
+ ELSE if_abap_behv=>fc-o-enabled )
+ ) TO result.
+
+ ENDIF.
+ ENDLOOP.
+ ENDMETHOD.
+
+ METHOD multiply_by_3.
+ "Retrieving instances based on requested keys
+ READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
+ ENTITY root
+ FIELDS ( field3 field4 ) WITH CORRESPONDING #( keys )
+ RESULT DATA(result)
+ FAILED failed.
+
+ "Setting %action value in failed response parameter
+ LOOP AT failed-root ASSIGNING FIELD-SYMBOL().
+ -%action-multiply_by_3 = if_abap_behv=>mk-on.
+ ENDLOOP.
+
+ "Multiply integer values by 3
+ MODIFY ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
+ ENTITY root
+ UPDATE FIELDS ( field3 field4 ) WITH VALUE #( FOR key IN result ( %tky = key-%tky
+ field3 = key-field3 * 3
+ field4 = key-field4 * 3 ) ).
+ ENDMETHOD.
+
+ METHOD get_global_features.
+ "The execution of an action should be disabled based on a certain time frame.
+ DATA(time1) = CONV t( '070000' ).
+ DATA(time2) = CONV t( '120000' ).
+
+ result = VALUE #( %action-set_z = COND #( WHEN cl_abap_context_info=>get_system_time( ) BETWEEN time1 AND time2
+ THEN if_abap_behv=>fc-o-enabled
+ ELSE if_abap_behv=>fc-o-disabled )
+ ).
+
+ IF result-%action-set_z = if_abap_behv=>fc-o-disabled.
+ APPEND VALUE #( %msg = new_message_with_text( text = 'Execution of action currently not allowed.'
+ severity = if_abap_behv_message=>severity-error )
+ %global = if_abap_behv=>mk-on ) TO reported-root.
+ ENDIF.
+ ENDMETHOD.
+
+ METHOD set_z.
+
+ "Retrieving instances based on requested keys
+ READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
+ ENTITY root
+ FIELDS ( field3 field4 ) WITH CORRESPONDING #( keys )
+ RESULT DATA(result)
+ FAILED failed.
+
+ "Setting %action value in failed response parameter
+ LOOP AT failed-root ASSIGNING FIELD-SYMBOL().
+ -%action-set_z = if_abap_behv=>mk-on.
+ ENDLOOP.
+
+ "Setting a field value
+ MODIFY ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
+ ENTITY root
+ UPDATE FIELDS ( field2 ) WITH VALUE #( FOR key IN result ( %tky = key-%tky
+ field2 = 'Z' ) ).
+
+ ENDMETHOD.
+
+ENDCLASS.
+
+***********************************************************************
+* Local saver class lsc_zdemo_abap_rap_ro_u
+*
+* Contains saver method definitions and implementations. The only
+* methods that are implemented in this example are the save method (to
+* persist the data to the database) and cleanup methods (to clear the
+* transactional buffer).
+*
+* The save method is implemented in a way, that the database tables
+* are modified based on the entries in the transactional buffer tables
+* and the flags for changed and deleted.
+* If the flag for changed is not initial, the instance gets created
+* or updated respectively in the database table.
+* If the flag for deleted is not initial, the database table entry
+* is deleted.
+*
+***********************************************************************
+
+CLASS lsc_zdemo_abap_rap_ro_u DEFINITION INHERITING FROM cl_abap_behavior_saver.
+ PROTECTED SECTION.
+
+ METHODS finalize REDEFINITION.
+
+ METHODS check_before_save REDEFINITION.
+
+ METHODS save REDEFINITION.
+
+ METHODS cleanup REDEFINITION.
+
+ METHODS cleanup_finalize REDEFINITION.
+
+ENDCLASS.
+
+CLASS lsc_zdemo_abap_rap_ro_u IMPLEMENTATION.
+
+ METHOD finalize.
+ ENDMETHOD.
+
+ METHOD check_before_save.
+ ENDMETHOD.
+
+ METHOD save.
+
+ "Processing the saving of create and update operations
+ "Only those entries should be saved to the database table whose flag for "changed" is not initial.
+ DATA mod_tab TYPE TABLE OF zdemo_abap_rap_ro_u.
+
+ IF line_exists( lcl_buffer=>root_buffer[ changed = abap_true ] ).
+ LOOP AT lcl_buffer=>root_buffer ASSIGNING FIELD-SYMBOL() WHERE changed = abap_true AND deleted = abap_false.
+ APPEND CORRESPONDING #( -instance ) TO mod_tab.
+ ENDLOOP.
+ MODIFY zdemo_abap_rapt1 FROM TABLE @( CORRESPONDING #( mod_tab ) ).
+ ENDIF.
+
+ "Processing the saving of delete operations
+ "Only those entries should be deleted from the database table whose flag "deleted" is not initial.
+ DATA del_tab TYPE lcl_buffer=>tt_root_keys.
+
+ IF line_exists( lcl_buffer=>root_buffer[ deleted = abap_true ] ).
+ LOOP AT lcl_buffer=>root_buffer ASSIGNING FIELD-SYMBOL() WHERE deleted = abap_true.
+ APPEND CORRESPONDING #( -instance ) TO del_tab.
+ ENDLOOP.
+ DELETE zdemo_abap_rapt1 FROM TABLE @( CORRESPONDING #( del_tab ) ).
+
+ "Processing database entries of child entity: When the parent instance is deleted,
+ "the corresponding child instances are deleted.
+ SELECT key_field, key_ch
+ FROM zdemo_abap_rapt2 AS db
+ WHERE EXISTS
+ ( SELECT * FROM @del_tab AS it
+ WHERE key_field = db~key_field )
+ INTO TABLE @DATA(child_keys).
+
+ "Deleting entries from database table
+ DELETE zdemo_abap_rapt2 FROM TABLE @( CORRESPONDING #( child_keys ) ).
+ "Deleting instances from child buffer
+ DELETE lcl_buffer=>child_buffer WHERE instance-key_field = -instance-key_field AND deleted = abap_true.
+ ENDIF.
+
+ "Processing the saving of create-by-association operations.
+ DATA cba_tab TYPE TABLE OF zdemo_abap_rap_ch_u.
+
+ IF line_exists( lcl_buffer=>child_buffer[ changed = abap_true ] ).
+ LOOP AT lcl_buffer=>child_buffer ASSIGNING FIELD-SYMBOL() WHERE changed = abap_true AND deleted = abap_false.
+ APPEND CORRESPONDING #( -instance ) TO cba_tab.
+ ENDLOOP.
+ MODIFY zdemo_abap_rapt2 FROM TABLE @( CORRESPONDING #( cba_tab ) ).
+ ENDIF.
+ ENDMETHOD.
+
+ METHOD cleanup.
+ "Clearing the transactional buffer.
+ CLEAR lcl_buffer=>root_buffer.
+ CLEAR lcl_buffer=>child_buffer.
+ ENDMETHOD.
+
+ METHOD cleanup_finalize.
+ ENDMETHOD.
+
+ENDCLASS.
+
+***********************************************************************
+* Local handler class for the child entity lhc_child
+*
+* Contains handler method definitions and implementations for read
+* and read-by-association operations.
+*
+***********************************************************************
+
+CLASS lhc_child DEFINITION INHERITING FROM cl_abap_behavior_handler.
+
+ PRIVATE SECTION.
+
+ METHODS read FOR READ
+ IMPORTING keys FOR READ child RESULT result.
+
+ METHODS rba_Parent FOR READ
+ IMPORTING keys_rba FOR READ child\_Parent FULL result_requested RESULT result LINK association_links.
+
+ENDCLASS.
+
+CLASS lhc_child IMPLEMENTATION.
+
+ METHOD read.
+ "Preparing the transactional buffer for child entity based on the input BDEF derived type.
+ "Here, the full_key flag is set to consider all key values.
+ "Purpose: The preparation method is set up to also consider the entries in the root buffer
+ "when dealing with by-association operations which is not relevant in this case.
+ lcl_buffer=>prep_child_buffer( VALUE #( FOR wa IN keys ( key_field = wa-key_field
+ key_ch = wa-key_ch
+ full_key = abap_true ) ) ).
+
+ "Processing the requested keys sequentially
+ LOOP AT keys ASSIGNING FIELD-SYMBOL() GROUP BY -%tky.
+
+ "Logic:
+ "- Line with the requested key values exists in the child buffer
+ "- If it is true: Adding the line to the RESULT parameter considering %control values.
+ READ TABLE lcl_buffer=>child_buffer
+ WITH KEY instance-key_field = -key_field
+ instance-key_ch = -key_ch
+ ASSIGNING FIELD-SYMBOL().
+
+ IF sy-subrc = 0 AND -deleted = abap_false.
+
+ APPEND VALUE #( %tky = -%tky
+ field_ch1 = COND #( WHEN -%control-field_ch1 NE if_abap_behv=>mk-off
+ THEN -instance-field_ch1 )
+ field_ch2 = COND #( WHEN -%control-field_ch2 NE if_abap_behv=>mk-off
+ THEN -instance-field_ch2 )
+ ) TO result.
+
+ ELSE.
+
+ "Filling FAILED and REPORTED response parameters
+ APPEND VALUE #( %tky = -%tky
+ %fail-cause = if_abap_behv=>cause-not_found
+ ) TO failed-child.
+
+ APPEND VALUE #( %tky = -%tky
+ %msg = new_message_with_text(
+ severity = if_abap_behv_message=>severity-error
+ text = 'Read operation failed (child entity).' )
+ ) TO reported-child.
+
+ ENDIF.
+ ENDLOOP.
+ ENDMETHOD.
+
+ METHOD rba_Parent.
+ "Preparing the transactional buffers for both the root and child entity based on the input BDEF derived type.
+ lcl_buffer=>prep_root_buffer( CORRESPONDING #( keys_rba ) ).
+ lcl_buffer=>prep_child_buffer( CORRESPONDING #( keys_rba ) ).
+
+ LOOP AT keys_rba ASSIGNING FIELD-SYMBOL() GROUP BY -%tky.
+ "Logic:
+ "- Line with the shared key value exists in buffer for the root entity and is not marked as deleted
+ "- Line with the full key exists in the child buffer
+ "- If it is true: Adding the instance to the RESULT parameter considering %control values
+ IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = -key_field deleted = abap_false ] )
+ AND line_exists( lcl_buffer=>child_buffer[ instance-key_field = -key_field instance-key_ch = -key_ch deleted = abap_false ] ).
+
+ "Filling the LINK parameter
+ INSERT VALUE #( target-%tky = CORRESPONDING #( -%tky )
+ source-%tky = VALUE #( key_field = -key_field
+ key_ch = -key_ch )
+ ) INTO TABLE association_links.
+
+ IF result_requested = abap_true.
+ READ TABLE lcl_buffer=>root_buffer
+ WITH KEY instance-key_field = -key_field
+ ASSIGNING FIELD-SYMBOL().
+
+ IF sy-subrc = 0.
+
+ APPEND VALUE #( %tky = CORRESPONDING #( -%tky )
+ field1 = COND #( WHEN -%control-field1 NE if_abap_behv=>mk-off
+ THEN -instance-field1 )
+ field2 = COND #( WHEN -%control-field2 NE if_abap_behv=>mk-off
+ THEN -instance-field2 )
+ field3 = COND #( WHEN