1022 lines
42 KiB
ABAP
1022 lines
42 KiB
ABAP
***********************************************************************
|
|
*
|
|
* 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 is intentionally kept
|
|
* short and simple and focuses on specific RAP aspects. For this reason,
|
|
* the example might not fully meet the requirements of the RAP BO contract.
|
|
*
|
|
* 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 only meant for supporting 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 and not to
|
|
* solve concrete programming tasks. For production application programs,
|
|
* a dedicated solution should therefore always be worked out for each
|
|
* individual case. There is no guarantee for either the correctness or
|
|
* the completeness of the code. In addition, there is no legal
|
|
* responsibility or liability for possible errors or their consequences
|
|
* which occur through the use of 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,
|
|
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.
|
|
|
|
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.
|
|
|
|
"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.
|
|
|
|
"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(<buffer>).
|
|
"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 = <buffer>-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 = @<buffer>-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 = @<buffer>-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(<buffer_ch>).
|
|
|
|
"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 <buffer_ch>-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 = <buffer_ch>-key_field
|
|
instance-key_ch = <buffer_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 = @<buffer_ch>-key_field
|
|
AND key_ch = @<buffer_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 = @<buffer_ch>-key_field
|
|
AND key_ch = @<buffer_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 = <buffer_ch>-key_field ] )
|
|
AND VALUE #( lcl_buffer=>root_buffer[ instance-key_field = <buffer_ch>-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 = @<buffer_ch>-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 = @<buffer_ch>-key_field
|
|
INTO CORRESPONDING FIELDS OF TABLE @ch_tab.
|
|
|
|
IF sy-subrc = 0.
|
|
|
|
LOOP AT ch_tab ASSIGNING FIELD-SYMBOL(<ch>).
|
|
"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 = <ch>-key_field
|
|
instance-key_ch = <ch>-key_ch ] ).
|
|
APPEND VALUE #( instance = <ch> ) 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 on purpose. No global authorization restriction.
|
|
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(<create>).
|
|
"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 = <create>-key_field ] )
|
|
OR line_exists( lcl_buffer=>root_buffer[ instance-key_field = <create>-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 = <create>-key_field ]-instance-key_field OPTIONAL ) AND deleted = abap_true.
|
|
|
|
"Adding new instance to the transactional buffer by considering %control values
|
|
APPEND VALUE #( cid = <create>-%cid
|
|
instance-key_field = <create>-key_field
|
|
instance-field1 = COND #( WHEN <create>-%control-field1 NE if_abap_behv=>mk-off
|
|
THEN <create>-field1 )
|
|
instance-field2 = COND #( WHEN <create>-%control-field2 NE if_abap_behv=>mk-off
|
|
THEN <create>-field2 )
|
|
instance-field3 = COND #( WHEN <create>-%control-field3 NE if_abap_behv=>mk-off
|
|
THEN <create>-field3 )
|
|
instance-field4 = COND #( WHEN <create>-%control-field4 NE if_abap_behv=>mk-off
|
|
THEN <create>-field4 )
|
|
changed = abap_true
|
|
deleted = abap_false ) TO lcl_buffer=>root_buffer.
|
|
|
|
"Filling the MAPPED response parameter for the root entity
|
|
INSERT VALUE #( %cid = <create>-%cid
|
|
%key = <create>-%key ) INTO TABLE mapped-root.
|
|
|
|
ELSE.
|
|
|
|
"Filling FAILED and REPORTED response parameters
|
|
APPEND VALUE #( %cid = <create>-%cid
|
|
%key = <create>-%key
|
|
%create = if_abap_behv=>mk-on
|
|
%fail-cause = if_abap_behv=>cause-unspecific
|
|
) TO failed-root.
|
|
|
|
APPEND VALUE #( %cid = <create>-%cid
|
|
%key = <create>-%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(<update>).
|
|
|
|
"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 = <update>-key_field
|
|
deleted = abap_false
|
|
ASSIGNING FIELD-SYMBOL(<fs_up>).
|
|
|
|
IF sy-subrc = 0.
|
|
<fs_up>-instance-field1 = COND #( WHEN <update>-%control-field1 NE if_abap_behv=>mk-off
|
|
THEN <update>-field1
|
|
ELSE <fs_up>-instance-field1 ).
|
|
|
|
<fs_up>-instance-field2 = COND #( WHEN <update>-%control-field2 NE if_abap_behv=>mk-off
|
|
THEN <update>-field2
|
|
ELSE <fs_up>-instance-field2 ).
|
|
|
|
<fs_up>-instance-field3 = COND #( WHEN <update>-%control-field3 NE if_abap_behv=>mk-off
|
|
THEN <update>-field3
|
|
ELSE <fs_up>-instance-field3 ).
|
|
|
|
<fs_up>-instance-field4 = COND #( WHEN <update>-%control-field4 NE if_abap_behv=>mk-off
|
|
THEN <update>-field4
|
|
ELSE <fs_up>-instance-field4 ).
|
|
|
|
<fs_up>-changed = abap_true.
|
|
<fs_up>-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(<read>) GROUP BY <read>-%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 = <read>-key_field
|
|
deleted = abap_false
|
|
ASSIGNING FIELD-SYMBOL(<fs_r>).
|
|
|
|
IF sy-subrc = 0.
|
|
|
|
APPEND VALUE #( %tky = <read>-%tky
|
|
field1 = COND #( WHEN <read>-%control-field1 NE if_abap_behv=>mk-off
|
|
THEN <fs_r>-instance-field1 )
|
|
field2 = COND #( WHEN <read>-%control-field2 NE if_abap_behv=>mk-off
|
|
THEN <fs_r>-instance-field2 )
|
|
field3 = COND #( WHEN <read>-%control-field3 NE if_abap_behv=>mk-off
|
|
THEN <fs_r>-instance-field3 )
|
|
field4 = COND #( WHEN <read>-%control-field4 NE if_abap_behv=>mk-off
|
|
THEN <fs_r>-instance-field4 )
|
|
) TO result.
|
|
|
|
ELSE.
|
|
|
|
"Filling FAILED and REPORTED response parameters
|
|
APPEND VALUE #( %tky = <read>-%tky
|
|
%fail-cause = if_abap_behv=>cause-not_found
|
|
) TO failed-root.
|
|
|
|
APPEND VALUE #( %tky = <read>-%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 = lr_key->*
|
|
%fail-cause = if_abap_behv=>cause-locked )
|
|
TO failed-root.
|
|
CATCH cx_abap_lock_failure.
|
|
APPEND VALUE #( %key = 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 ) ).
|
|
|
|
"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(<delete>).
|
|
"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 = <delete>-key_field
|
|
deleted = abap_false
|
|
ASSIGNING FIELD-SYMBOL(<fs_del>).
|
|
|
|
IF sy-subrc = 0.
|
|
|
|
<fs_del>-changed = abap_false.
|
|
<fs_del>-deleted = abap_true.
|
|
|
|
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(<rba>) GROUP BY <rba>-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 = <rba>-key_field deleted = abap_false ] )
|
|
AND line_exists( lcl_buffer=>child_buffer[ instance-key_field = <rba>-key_field ] ).
|
|
|
|
LOOP AT lcl_buffer=>child_buffer ASSIGNING FIELD-SYMBOL(<ch>) WHERE instance-key_field = <rba>-key_field.
|
|
|
|
"Filling the table for the LINK parameter
|
|
INSERT VALUE #( source-%tky = <rba>-%tky
|
|
target-%tky = VALUE #( key_field = <ch>-instance-key_field
|
|
key_ch = <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 #( %tky = <rba>-%tky
|
|
key_ch = COND #( WHEN <rba>-%control-key_ch NE if_abap_behv=>mk-off
|
|
THEN <ch>-instance-key_ch )
|
|
field_ch1 = COND #( WHEN <rba>-%control-field_ch1 NE if_abap_behv=>mk-off
|
|
THEN <ch>-instance-field_ch1 )
|
|
field_ch2 = COND #( WHEN <rba>-%control-field_ch2 NE if_abap_behv=>mk-off
|
|
THEN <ch>-instance-field_ch2 )
|
|
) TO result.
|
|
ENDIF.
|
|
ENDLOOP.
|
|
|
|
ELSE.
|
|
|
|
"Filling FAILED and REPORTED response parameters
|
|
APPEND VALUE #( %tky = <rba>-%tky
|
|
%fail-cause = if_abap_behv=>cause-not_found
|
|
%assoc-_child = if_abap_behv=>mk-on
|
|
) TO failed-root.
|
|
|
|
|
|
APPEND VALUE #( %tky = <rba>-%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(<cba>) GROUP BY <cba>-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 = <cba>-key_field deleted = abap_false ] ).
|
|
|
|
LOOP AT <cba>-%target ASSIGNING FIELD-SYMBOL(<ch>).
|
|
|
|
"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 = <cba>-key_field
|
|
instance-key_ch = <ch>-key_ch ] )
|
|
AND <ch>-key_ch IS NOT INITIAL.
|
|
|
|
APPEND VALUE #( cid_ref = <cba>-%cid_ref
|
|
cid_target = <ch>-%cid
|
|
instance-key_field = <cba>-key_field
|
|
instance-key_ch = <ch>-key_ch
|
|
instance-field_ch1 = COND #( WHEN <ch>-%control-field_ch1 NE if_abap_behv=>mk-off
|
|
THEN <ch>-field_ch1 )
|
|
instance-field_ch2 = COND #( WHEN <ch>-%control-field_ch2 NE if_abap_behv=>mk-off
|
|
THEN <ch>-field_ch2 )
|
|
changed = abap_true
|
|
) TO lcl_buffer=>child_buffer.
|
|
|
|
"Filling MAPPED response parameter
|
|
INSERT VALUE #( %cid = <ch>-%cid
|
|
%key = VALUE #( key_field = <cba>-key_field
|
|
key_ch = <ch>-key_ch ) ) INTO TABLE mapped-child.
|
|
|
|
ELSE.
|
|
|
|
"Filling FAILED and REPORTED response parameters
|
|
APPEND VALUE #( %cid = <cba>-%cid_ref
|
|
%tky = <cba>-%tky
|
|
%assoc-_child = if_abap_behv=>mk-on
|
|
%fail-cause = if_abap_behv=>cause-unspecific
|
|
) TO failed-root.
|
|
|
|
APPEND VALUE #( %cid = <cba>-%cid_ref
|
|
%tky = <cba>-%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 = <ch>-%cid
|
|
%key = VALUE #( key_field = <cba>-key_field key_ch = <ch>-key_ch )
|
|
%fail-cause = if_abap_behv=>cause-dependency
|
|
) TO failed-child.
|
|
|
|
APPEND VALUE #( %cid = <ch>-%cid
|
|
%key = VALUE #( key_field = <cba>-key_field key_ch = <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 = <cba>-%cid_ref
|
|
%tky = <cba>-%tky
|
|
%assoc-_child = if_abap_behv=>mk-on
|
|
%fail-cause = if_abap_behv=>cause-not_found
|
|
) TO failed-root.
|
|
|
|
APPEND VALUE #( %cid = <cba>-%cid_ref
|
|
%tky = <cba>-%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 <cba>-%target ASSIGNING FIELD-SYMBOL(<target>).
|
|
APPEND VALUE #( %cid = <target>-%cid
|
|
%key = <target>-%key
|
|
%fail-cause = if_abap_behv=>cause-dependency
|
|
) TO failed-child.
|
|
|
|
APPEND VALUE #( %cid = <target>-%cid
|
|
%key = <target>-%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(<f>).
|
|
<f>-%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(<auth>).
|
|
|
|
"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 = <auth>-%tky
|
|
%op = VALUE #( %delete = COND #( WHEN <auth>-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(<feat>).
|
|
|
|
"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 = <feat>-%tky
|
|
%features-%action-multiply_by_3 = COND #( WHEN <feat>-field3 = 0 OR <feat>-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(<f>).
|
|
<f>-%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(<f>).
|
|
<f>-%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(<cr>) WHERE changed = abap_true AND deleted = abap_false.
|
|
|
|
APPEND CORRESPONDING #( <cr>-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(<del>) WHERE deleted = abap_true.
|
|
|
|
APPEND CORRESPONDING #( <del>-instance ) TO del_tab.
|
|
|
|
ENDLOOP.
|
|
|
|
DELETE zdemo_abap_rapt1 FROM TABLE @( CORRESPONDING #( del_tab ) ).
|
|
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(<cba>) WHERE changed = abap_true.
|
|
|
|
APPEND CORRESPONDING #( <cba>-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(<read>) GROUP BY <read>-%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 = <read>-key_field
|
|
instance-key_ch = <read>-key_ch
|
|
ASSIGNING FIELD-SYMBOL(<fs_rc>).
|
|
|
|
IF sy-subrc = 0.
|
|
|
|
APPEND VALUE #( %tky = <read>-%tky
|
|
field_ch1 = COND #( WHEN <read>-%control-field_ch1 NE if_abap_behv=>mk-off
|
|
THEN <fs_rc>-instance-field_ch1 )
|
|
field_ch2 = COND #( WHEN <read>-%control-field_ch2 NE if_abap_behv=>mk-off
|
|
THEN <fs_rc>-instance-field_ch2 )
|
|
) TO result.
|
|
|
|
ELSE.
|
|
|
|
"Filling FAILED and REPORTED response parameters
|
|
APPEND VALUE #( %tky = <read>-%tky
|
|
%fail-cause = if_abap_behv=>cause-not_found
|
|
) TO failed-child.
|
|
|
|
APPEND VALUE #( %tky = <read>-%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(<rba>) GROUP BY <rba>-%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 = <rba>-key_field deleted = abap_false ] )
|
|
AND line_exists( lcl_buffer=>child_buffer[ instance-key_field = <rba>-key_field instance-key_ch = <rba>-key_ch ] ).
|
|
|
|
"Filling the LINK parameter
|
|
INSERT VALUE #( target-%tky = <rba>-%tky
|
|
source-%tky = VALUE #( key_field = <rba>-key_field
|
|
key_ch = <rba>-key_ch )
|
|
) INTO TABLE association_links.
|
|
|
|
IF result_requested = abap_true.
|
|
READ TABLE lcl_buffer=>root_buffer
|
|
WITH KEY instance-key_field = <rba>-key_field
|
|
ASSIGNING FIELD-SYMBOL(<fs_rp>).
|
|
|
|
IF sy-subrc = 0.
|
|
|
|
APPEND VALUE #( %tky = <rba>-%tky
|
|
field1 = COND #( WHEN <rba>-%control-field1 NE if_abap_behv=>mk-off
|
|
THEN <fs_rp>-instance-field1 )
|
|
field2 = COND #( WHEN <rba>-%control-field2 NE if_abap_behv=>mk-off
|
|
THEN <fs_rp>-instance-field2 )
|
|
field3 = COND #( WHEN <rba>-%control-field3 NE if_abap_behv=>mk-off
|
|
THEN <fs_rp>-instance-field3 )
|
|
field4 = COND #( WHEN <rba>-%control-field4 NE if_abap_behv=>mk-off
|
|
THEN <fs_rp>-instance-field4 )
|
|
) TO result.
|
|
ENDIF.
|
|
ENDIF.
|
|
|
|
ELSE.
|
|
|
|
"Filling FAILED and REPORTED response parameters
|
|
APPEND VALUE #( %tky = <rba>-%tky
|
|
%assoc-_parent = if_abap_behv=>mk-on
|
|
%fail-cause = if_abap_behv=>cause-not_found
|
|
) TO failed-child.
|
|
|
|
|
|
APPEND VALUE #( %tky = <rba>-%tky
|
|
%msg = new_message_with_text(
|
|
severity = if_abap_behv_message=>severity-error
|
|
text = 'RBA (child to parent) failed.' )
|
|
) TO reported-child.
|
|
|
|
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.
|
|
|
|
ENDCLASS.
|