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”:

    1. 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

 

    1. 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

 

    1. 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)

 

    1. 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.

 

    1. 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.

 

    1. 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' ).

 

    1. 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| )
       ).

 

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
Sara Sampaio

Sara Sampaio

Author Since: March 10, 2022

0 0 votes
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x