The generated Custom Business Object (CBO) User Interface only allows deletion of single entries. This blog post explains how you can delete all CBO entries at once or how to delete multiple entries based on deletion rules.
User Interface Overview
You need to create an own CBO that will serve as “Deletion CBO” for all of your CBOs. An end user will select in which CBO they want to delete entries. And in a second step they can either delete all data or define several deletion rules and execute those.
Deletion CBO Setup
Execute the following steps to build the “Deletion CBO”:
-
- Create a Custom Code List YY1_SIGN with the code values I for Include and E for Exclude. Create a second Custom Code List YY1_OPERATOR with the following entries:
Code Description EQ Equal GE Greater Than or Equal GT Greater Than LE Lower Than or Equal LT Lower Than NE Not Equal
- Create a Custom Code List YY1_SIGN with the code values I for Include and E for Exclude. Create a second Custom Code List YY1_OPERATOR with the following entries:
-
- Create a new CBO YY1_MASS_DELETE. The name of the CBO is important if you want to copy the code of this blog post. The root node consists of the following fields:
Label Identifier Type Key Read Only CBO Name CBOName Text (Length 30) X Deletion Last Executed On LastExecutedOn Date X All Data Deleted DeletedAll Checkbox X
- Create a new CBO YY1_MASS_DELETE. The name of the CBO is important if you want to copy the code of this blog post. The root node consists of the following fields:
-
- Create a subnode DeletionRule with the following fields:
Label Identifier Type Field Name FieldName Text (Length 30) Sign Sign Code List YY1_SIGN Option Operator Code List YY1_OPERATOR Value Value Text (Length 100)
- Create a subnode DeletionRule with the following fields:
-
- Mark the CBO for UI Generation, publish your CBO and maintain the Catalog Extension. As next step, in order to allow mass deletion and deletion based on deletion rules, you will now maintain two actions in the tab ‘Logic’. The action will automatically appear as buttons on your generated UI.
-
- Create the action “DeleteAllData” with Label “Delete All CBO Data”. Also create the action “DeleteRuleBased” with Label “Execute Deletion Rules”. Publish your CBO. After publishing, you are able to define the logic for your actions.
-
- Open the logic for the action “DeleteAllData”. In the section ‘Select entries’ you have to add each CBO which you want to enable for deletion. In case of this blog post the CBOs YY1_USER and YY1_BUILDING are enabled. Publish your action logic after implementation.
* Action DeleteAllData for Node ID MASS_DELETE * * Importing Parameter : association (Navigation to Parent/Child/ * Associated Node Instances) * write (API for creating and updating * Custom Business Object Node Instances) * Changing Parameter : MASS_DELETE (Current Node Data) * Exporting Parameter : message (Message with Severity S(uccess), * W(arning), E(rror)) DATA: lv_timezone TYPE timezone. CONSTANTS: lc_package_size TYPE i VALUE 5000. *----------------------------------------------------------------------* * Select entries *----------------------------------------------------------------------* CASE mass_delete-cboname. WHEN 'YY1_USER'. SELECT sap_uuid FROM yy1_user INTO TABLE @DATA(lt_keys). WHEN 'YY1_BUILDING'. SELECT sap_uuid FROM yy1_building INTO TABLE @lt_keys. WHEN OTHERS. message = VALUE #( severity = co_severity-error text = |CBO { mass_delete-cboname } | & |not enabled for deletion| ). RETURN. ENDCASE. DATA(lv_lines) = LINES( lt_keys ). DATA(lv_all_lines) = lv_lines. *----------------------------------------------------------------------* * Delete entries in 5000 chunks -> Prevent System Overloading *----------------------------------------------------------------------* DATA(lv_packaging) = abap_false. IF lv_lines > lc_package_size. lv_packaging = abap_true. ENDIF. DATA(lv_end_delete) = abap_false. WHILE lv_end_delete = abap_false. IF lv_lines <= lc_package_size. lv_end_delete = abap_true. ENDIF. DATA(lt_current_keys) = lt_keys. IF lv_lines > lc_package_size. DATA(lv_delete_index) = lc_package_size + 1. DELETE lt_current_keys FROM lv_delete_index. DELETE lt_keys FROM 1 TO lc_package_size. lv_lines = lines( lt_keys ). ENDIF. TRY. write->delete_root( EXPORTING business_object_id = CONV #( mass_delete-cboname ) keys = lt_current_keys ). CATCH cx_root. message = VALUE #( severity = co_severity-error text = 'Deletion of Entries failed' ). RETURN. ENDTRY. ENDWHILE. *----------------------------------------------------------------------* * Update Last Executed at Column and Delete All indicator *----------------------------------------------------------------------* GET TIME STAMP FIELD DATA(lv_current_time). CONVERT TIME STAMP lv_current_time TIME ZONE lv_timezone INTO DATE DATA(lv_current_date). mass_delete-lastexecutedon = lv_current_date. mass_delete-deletedall = abap_true. *----------------------------------------------------------------------* * Success Message *----------------------------------------------------------------------* message = VALUE #( severity = co_severity-success text = 'All data deleted successfully' ).
- Open the logic for the action “DeleteAllData”. In the section ‘Select entries’ you have to add each CBO which you want to enable for deletion. In case of this blog post the CBOs YY1_USER and YY1_BUILDING are enabled. Publish your action logic after implementation.
-
- Open the logic for the action “DeleteRuleBased”. You have to implement the following sections:
-
- Data declarations per field name: Define a range table for each field, you want to enable for rule based deletion.
-
- Build Range Tables: Add each CBO with its field names which you want to enable for deletion. In case of this blog post the CBO YY1_USER is enabled. Deletion rules for CBO YY1_BUILDING would fail as they are not implemented.
-
- Select Entries: Add the select statement for each enabled CBO with the corresponding range tables.
Publish your action logic after implementation.
* Action DeleteRuleBased for Node ID MASS_DELETE * * Importing Parameter : association (Navigation to Parent/Child/ * Associated Node Instances) * write (API for creating and updating * Custom Business Object Node Instances) * Changing Parameter : MASS_DELETE (Current Node Data) * Exporting Parameter : message (Message with Severity S(uccess), * W(arning), E(rror)) DATA: lv_timezone TYPE timezone. *-----------------------------------------------------------------------* * Data declarations per field name *-----------------------------------------------------------------------* DATA: lt_range_city TYPE RANGE OF yy1_s_yy1_user_d-city, lt_range_country TYPE RANGE OF yy1_s_yy1_user_d-country. CONSTANTS: lc_package_size TYPE i VALUE 5000. *-----------------------------------------------------------------------* * Allowed Values Check -> Prevent Dump *-----------------------------------------------------------------------* DATA(lt_allowed_values_sign) = VALUE string_table( ( `I` ) ( `E` ) ). DATA(lt_allowed_values_option) = VALUE string_table( ( `EQ` ) ( `NE` ) ( `CP` ) ( `LE` ) ( `GE` ) ( `NP` ) ( `GT` ) ( `LT` ) ). *-----------------------------------------------------------------------* * Check Deletion Rules *-----------------------------------------------------------------------* DATA(lt_rules) = association->to_deletionrule( ). IF lines( lt_rules ) = 0. message = VALUE #( severity = co_severity-error text = 'No deletion rules defined' ). RETURN. ENDIF. LOOP AT lt_rules INTO DATA(ls_rule). IF NOT line_exists( lt_allowed_values_sign[ table_line = ls_rule-deletionrule-sign ] ). message = VALUE #( severity = co_severity-error text = |Sign { ls_rule-deletionrule-sign } | & |is not allowed| ). RETURN. ENDIF. IF NOT line_exists( lt_allowed_values_option[ table_line = ls_rule-deletionrule-operator ] ). message = VALUE #( severity = co_severity-error text = |Option | & |{ ls_rule-deletionrule-operator } | & |is not allowed| ). RETURN. ENDIF. *-----------------------------------------------------------------------* * Build Range Tables *-----------------------------------------------------------------------* DATA(lv_no_fieldname) = abap_false. CASE mass_delete-cboname. WHEN 'YY1_USER'. CASE ls_rule-deletionrule-fieldname. WHEN 'City'. APPEND VALUE #( sign = ls_rule-deletionrule-sign option = ls_rule-deletionrule-operator low = ls_rule-deletionrule-value ) TO lt_range_city. WHEN 'Country'. APPEND VALUE #( sign = ls_rule-deletionrule-sign option = ls_rule-deletionrule-operator low = ls_rule-deletionrule-value ) TO lt_range_country. WHEN OTHERS. lv_no_fieldname = abap_true. ENDCASE. WHEN OTHERS. message = VALUE #( severity = co_severity-error text = |Deletion for CBO | & |{ mass_delete-cboname } | & |is not implemented| ). RETURN. ENDCASE. IF lv_no_fieldname = abap_true. message = VALUE #( severity = co_severity-error text = |Fieldname | & |{ ls_rule-deletionrule-fieldname } | & |does not exist in CBO | & |{ mass_delete-cboname } | ). RETURN. ENDIF. ENDLOOP. *-----------------------------------------------------------------------* * Select entries *-----------------------------------------------------------------------* CASE mass_delete-cboname. WHEN 'YY1_USER'. SELECT sap_uuid FROM yy1_user INTO TABLE @DATA(lt_keys) WHERE city IN @lt_range_city AND country IN @lt_range_country. SELECT count( sap_uuid ) FROM yy1_user INTO @DATA(lv_db_lines). WHEN OTHERS. message = VALUE #( severity = co_severity-error text = |Deletion for CBO | & |{ mass_delete-cboname } | & |is not implemented| ). RETURN. ENDCASE. DATA(lv_lines) = LINES( lt_keys ). DATA(lv_all_lines) = lv_lines. IF lv_lines = 0. message = VALUE #( severity = co_severity-success text = 'No entries match the deletion rules' ). RETURN. ENDIF. *-----------------------------------------------------------------------* * Delete entries in 5000 chunks -> Prevent System Overloading *-----------------------------------------------------------------------* IF lv_db_lines = lv_all_lines. CLEAR lt_keys. write->get_context_node( )->execute_action( EXPORTING action_id = 'DeleteAllData' IMPORTING data = mass_delete " messages = " Action messages are returned anyhow to the UI ). mass_delete-deletedall = abap_false. RETURN. ENDIF. DATA(lv_packaging) = abap_false. IF lv_lines > lc_package_size. lv_packaging = abap_true. ENDIF. DATA(lv_end_delete) = abap_false. WHILE lv_end_delete = abap_false. IF lv_lines <= lc_package_size. lv_end_delete = abap_true. ENDIF. DATA(lt_current_keys) = lt_keys. IF lv_lines > lc_package_size. DATA(lv_delete_index) = lc_package_size + 1. DELETE lt_current_keys FROM lv_delete_index. DELETE lt_keys FROM 1 TO lc_package_size. lv_lines = lines( lt_keys ). ENDIF. TRY. write->delete_root( EXPORTING business_object_id = CONV #( mass_delete-cboname ) keys = lt_current_keys ). CATCH cx_root. message = VALUE #( severity = co_severity-error text = 'Deletion of Entries failed' ). RETURN. ENDTRY. ENDWHILE. *-----------------------------------------------------------------------* * Update Last Executed at Column *-----------------------------------------------------------------------* GET TIME STAMP FIELD DATA(lv_current_time). CONVERT TIME STAMP lv_current_time TIME ZONE lv_timezone INTO DATE DATA(lv_current_date). mass_delete-lastexecutedon = lv_current_date. *-----------------------------------------------------------------------* * Success Message *-----------------------------------------------------------------------* message = VALUE #( severity = co_severity-success text = COND #( WHEN lv_all_lines = 1 THEN |1 entry deleted successfully| ELSE |{ lv_all_lines } entries deleted successfully| ) ).
-
- Open the logic for the action “DeleteRuleBased”. You have to implement the following sections:
Deletion through OData Service
For both actions a function import was created in the generated OData Service – YY1_MASS_DELETEDeletealldata and YY1_MASS_DELETEDeleterulebased. You can call these function imports to delete your CBO data from the outside of the Marketing system, e.g. through SAP Cloud Platform Integration (Example for scheduled Function Import call). You will need the SAP_UUID of your root node entry. To get this, use the following call with your CBO Name:
GET https://my<...>-api.s4hana.ondemand.com/sap/opu/odata/sap/YY1_MASS_DELETE_CDS/YY1_MASS_DELETE?$selec... eq 'YY1_USER'
Afterwards you can e.g. delete all data using this call with the returned SAP_UUID:
POST https://my<...>-api.s4hana.ondemand.com/sap/opu/odata/sap/YY1_MASS_DELETE_CDS/YY1_MASS_DELE