Skip to Content
Technical Articles

How to delete multiple CBO entries

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
  2. 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 20) X
    Deletion Last Executed On LastExecutedOn Date X
    All Data Deleted DeletedAll Checkbox X
  3. Create a subnode DeletionRule with the following fields:
    Label Identifier Type
    Field Name FieldName Text (Length 20)
    Sign Sign Code List YY1_SIGN
    Option Operator Code List YY1_OPERATOR
    Value Value Text (Length 100)
  4. 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.
  5. 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.
  6. 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' ).
    ​
  7. 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?$select=SAP_UUID&$filter=CBOName 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_DELETEDeletealldata?SAP_UUID=guid'00163e69-ebdd-1ee9-9785-c254cae1b958'
1 Comment
You must be Logged on to comment or reply to a post.