Custom program to mass cancel workflow items

Mass cancellation of work items is possible with the SWIA transaction as explained in this blog.

This blog will explain how you can use Z program to mass close like SWIA, but then in batch job mode, and automatically without user interaction.

The custom program

The custom program ZRSWIWILS_WI_CANCEL is basically a copy of standard SAP program RSWIWILS.

You can copy and paste the code from below source code text and add the text symbols and selection screen parameter text.

Start screen has 1 extra option more than standard SAP:

The program working explained

When the new option is selected, after the data fetching, the program simply calls function module SWW_WI_ADMIN_CANCEL to cancel the item. Since it is done in loop and without user interaction, the program can run in batch mode.

The custom program source code text

* Report ZRSWIWILS_WI_CANCEL
*----------------- Standard program RSWIWILS copy ---------------------*
*---------- This code is to extend existing functionality--------------*

REPORT zrswiwils_wi_cancel MESSAGE-ID swf_rep_base.

INCLUDE rswlfcod.
INCLUDE rswuincl.
CLASS cl_swf_rdg_dispatcher DEFINITION LOAD.

************************************************************************
*  Begin of Data                                                       *
************************************************************************
DATA: g_list_cnt TYPE sy-tabix.
DATA: zlt_wiheader TYPE swfatalvitm.
DATA: int TYPE REF TO if_swf_rep_workitem_selection.
DATA: tcode LIKE sy-tcode.
DATA: g_windows_titlebar TYPE string.
CONSTANTS: zlc_tcode  TYPE sytcode VALUE 'SWIA'.
*- type pools
TYPE-POOLS: slis.
TABLES: swfawrkitm.

*- select options
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-a01.

SELECT-OPTIONS: id FOR swfawrkitm-wi_id.
SELECTION-SCREEN END OF BLOCK b1.

SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE TEXT-a02.
SELECT-OPTIONS:
    type   FOR swfawrkitm-wi_type,
    state  FOR swfawrkitm-wi_stat,
    prio   FOR swfawrkitm-wi_prio,
    dhsta  FOR swfawrkitm-wi_dh_stat,
    task   FOR swfawrkitm-wi_rh_task,
    taskg  FOR swfawrkitm-wi_rh_task NO INTERVALS.
PARAMETERS: top_only TYPE xfeld.
SELECTION-SCREEN END OF BLOCK b2.

SELECTION-SCREEN BEGIN OF BLOCK b3 WITH FRAME TITLE TEXT-a03.
SELECT-OPTIONS:
    cd FOR swfawrkitm-wi_cd,
    ct FOR swfawrkitm-wi_ct NO-EXTENSION.
SELECTION-SCREEN END OF BLOCK b3.

SELECTION-SCREEN BEGIN OF BLOCK b6 WITH FRAME TITLE TEXT-a06.
PARAMETERS: p_cancel TYPE xfeld DEFAULT ' '.
SELECTION-SCREEN END OF BLOCK b6.

SELECTION-SCREEN BEGIN OF BLOCK b4 WITH FRAME TITLE TEXT-a04.
PARAMETERS: p_more TYPE swi_params-option AS CHECKBOX.
PARAMETERS: filter TYPE swf_utl002-clsname NO-DISPLAY.
PARAMETERS: p_swia TYPE xfeld NO-DISPLAY DEFAULT space. " note 1274031
SELECTION-SCREEN END OF BLOCK b4.

SELECTION-SCREEN BEGIN OF BLOCK b5 WITH FRAME TITLE TEXT-a05.
PARAMETERS: p_maxsel TYPE tbmaxsel.
SELECTION-SCREEN END OF BLOCK b5.

*-------------------------------------------------------------
INITIALIZATION.
*-------------------------------------------------------------
  cd-low = sy-datum.
  cd-sign = 'I'.
  cd-option = 'EQ'.
  APPEND cd.

*-------------------------------------------------------------
*- F4 functionality
*-------------------------------------------------------------
AT SELECTION-SCREEN ON VALUE-REQUEST FOR task-low.
  DATA: act_object_ext TYPE rhobjects-object.

  CALL FUNCTION 'RH_SEARCH_TASK'
    IMPORTING
      act_object_ext         = act_object_ext
    EXCEPTIONS
      no_active_plvar        = 1
      no_org_object_selected = 2
      no_valid_task_type     = 3
      OTHERS                 = 4.
  IF sy-subrc EQ 0.
    task-low = act_object_ext.
  ENDIF.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR task-high.
  DATA: act_object_ext TYPE rhobjects-object.

  CALL FUNCTION 'RH_SEARCH_TASK'
    IMPORTING
      act_object_ext         = act_object_ext
    EXCEPTIONS
      no_active_plvar        = 1
      no_org_object_selected = 2
      no_valid_task_type     = 3
      OTHERS                 = 4.
  IF sy-subrc EQ 0.
    task-high = act_object_ext.
  ENDIF.

*-------------------------------------------------------------
START-OF-SELECTION.
*-------------------------------------------------------------
  PERFORM main USING p_more p_maxsel.

*---------------------------------------------------------------------*
*       FORM MAIN                                                     *
*---------------------------------------------------------------------*
*       ........                                                      *
*---------------------------------------------------------------------*
FORM main USING p_more TYPE xfeld
                p_maxsel TYPE tbmaxsel.
  DATA: field_lst    TYPE slis_t_fieldcat_alv,
        field_cat    TYPE slis_fieldcat_alv,
        is_layout    TYPE slis_layout_alv,
        is_variant   LIKE disvariant,
        it_sort      TYPE slis_t_sortinfo_alv,
        l_string     TYPE string,
        l_grid_title TYPE lvc_title.

*- prepare the list format (determine columns...)
  PERFORM prepare_format CHANGING field_lst
                                  field_cat
                                  is_layout
                                  it_sort.
  is_variant-report = sy-repid.

*- get the list from the database.
  PERFORM get_workitem_header USING    p_more
                                       p_maxsel
                              CHANGING zlt_wiheader.
*- check empty
  DATA: hits TYPE i.
  DESCRIBE TABLE  zlt_wiheader LINES hits.
  IF NOT hits IS INITIAL.

*- set table layout
    is_layout-cell_merge = 'X'.

*- set title
    is_layout-window_titlebar =
                  'Workitems Cancel'(001).

    g_windows_titlebar = is_layout-window_titlebar.
    PERFORM get_title USING hits
                            g_windows_titlebar
                            l_grid_title.
    is_layout-window_titlebar = l_grid_title.

    IF p_cancel IS NOT INITIAL AND zlt_wiheader IS NOT INITIAL.
* Cancel the work item
      PERFORM cancel_workitem TABLES zlt_wiheader.
    ENDIF.
*- call the CO function to display the list.
    CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
      EXPORTING
        i_callback_program      = CONV syrepid( 'ZRSWIWILS_WI_CANCEL' )
        i_callback_user_command = 'CALL_UCOMM_WILIST'
        is_layout               = is_layout
        it_fieldcat             = field_lst
        it_sort                 = it_sort
        is_variant              = is_variant
      TABLES
        t_outtab                = zlt_wiheader
      EXCEPTIONS
        OTHERS                  = 1.
  ELSE.
    MESSAGE s003.
  ENDIF.

ENDFORM.                    "main

*---------------------------------------------------------------------*
*       FORM PREPARE_FORMAT                                           *
*---------------------------------------------------------------------*
*       ........                                                      *
*---------------------------------------------------------------------*
*  -->  FIELD_LST                                                     *
*  -->  FIELD_CAT                                                     *
*  -->  IS_LAYOUT                                                     *
*---------------------------------------------------------------------*
FORM prepare_format CHANGING field_lst TYPE slis_t_fieldcat_alv
                                 field_cat TYPE slis_fieldcat_alv
                                 is_layout TYPE slis_layout_alv
                                 it_sort   TYPE slis_t_sortinfo_alv.
  DATA: is_sort LIKE LINE OF it_sort.
  DATA: structure_name TYPE dd02l-tabname VALUE 'SWFAWRKITM'.
  DATA: lt_fieldcat TYPE lvc_t_fcat.
  DATA: ls_fieldcat TYPE lvc_s_fcat.
  DATA: wf_settings TYPE swp_admin.
  DATA: lh_aging_services TYPE REF TO cl_sww_daag_services.
  FIELD-SYMBOLS: <field_lst> LIKE LINE OF field_lst.

  is_layout-box_fieldname = 'B_MARKED'.
  is_layout-box_tabname   = 'WIHEADER'.

*- prepare sort
  PERFORM set_sort_tab CHANGING it_sort.

*- get fieldcatalog
  CALL FUNCTION 'LVC_FIELDCATALOG_MERGE' 
    EXPORTING
      i_structure_name       = structure_name
    CHANGING
      ct_fieldcat            = lt_fieldcat
    EXCEPTIONS
      inconsistent_interface = 1
      program_error          = 2
      OTHERS                 = 3.

  LOOP AT lt_fieldcat INTO ls_fieldcat.
    IF ls_fieldcat-fieldname EQ 'WI_RHTEXT'.
      ls_fieldcat-lowercase = 'X'.
    ENDIF.
    MOVE-CORRESPONDING ls_fieldcat TO field_cat.
    field_cat-seltext_s = ls_fieldcat-scrtext_s.
    field_cat-seltext_l = ls_fieldcat-scrtext_l.
    field_cat-seltext_m = ls_fieldcat-scrtext_m.
    APPEND field_cat TO field_lst.
  ENDLOOP.

*- no reference for B_MARKED
  CLEAR field_cat-ref_tabname.
  field_cat-tech = 'X'.
  field_cat-fieldname = 'B_MARKED'.
  APPEND field_cat TO field_lst.
  CLEAR field_cat-tech.

*- set columns positions
  LOOP AT field_lst ASSIGNING <field_lst>.
    CASE <field_lst>-fieldname.
      WHEN 'WI_ID'.
        <field_lst>-col_pos = 1.
      WHEN 'WI_STAT'.
        <field_lst>-col_pos = 2.
      WHEN 'WI_CHCKWI'.
        <field_lst>-col_pos = 3.
      WHEN 'TYPETEXT'.
        <field_lst>-col_pos = 4.
        <field_lst>-outputlen = 15.
      WHEN 'WI_RH_TASK'.
        <field_lst>-col_pos = 5.
        <field_lst>-outputlen = 11.
      WHEN 'WI_CD'.
        <field_lst>-col_pos = 6.
        <field_lst>-outputlen = 10.
      WHEN 'WI_CT'.
        <field_lst>-col_pos = 7.
        <field_lst>-outputlen = 8.
      WHEN 'WI_TEXT'.
        <field_lst>-col_pos = 8.
        <field_lst>-outputlen = 80.
      WHEN 'WI_CONFIRM'.
        <field_lst>-col_pos = 9.
      WHEN 'WI_REJECT'.
        <field_lst>-col_pos = 10.
      WHEN 'WI_PRIOTXT'.
        <field_lst>-col_pos = 11.
      WHEN 'RETRY_CNT'.
        <field_lst>-col_pos = 12.
      WHEN 'TOP_TASK'.
        <field_lst>-col_pos = 13.
      WHEN OTHERS.
        <field_lst>-col_pos = 999999.
    ENDCASE.
  ENDLOOP.
  SORT field_lst BY col_pos.
  LOOP AT field_lst ASSIGNING <field_lst>.
    <field_lst>-col_pos = sy-tabix.
  ENDLOOP.

*- set standard layout
  CLEAR field_cat.
  field_cat-no_out = 'X'.
  MODIFY field_lst FROM field_cat TRANSPORTING no_out
     WHERE fieldname EQ 'WI_LANG' OR
           fieldname EQ 'WI_TYPE' OR
           fieldname EQ 'VERSION' OR
           fieldname EQ 'WI_PRIO' OR
           fieldname EQ 'NOTE_CNT' OR
           fieldname EQ 'WI_RELEASE' OR
           fieldname EQ 'STATUSTEXT' OR
           fieldname EQ 'TCLASS' OR
           fieldname EQ 'WI_DH_STAT' OR
           fieldname EQ 'RETRY_CNT' OR
           fieldname EQ 'WLC_FLAGS' OR
           fieldname EQ 'TOP_TASK' OR
           fieldname EQ 'AGING_STATE' OR
           fieldname EQ 'AGING_TEMPERATURE'.

*- delete aging fields if not applicable
  lh_aging_services = cl_sww_daag_services=>get_instance( ).
  IF lh_aging_services->aging_enabled( ) NE 'X'.
    DELETE field_lst WHERE fieldname EQ 'AGING_STATE'
                        OR fieldname EQ 'AGING_TEMPERATURE'.
  ENDIF.

*- set checkboxes
  CLEAR field_cat.
  field_cat-checkbox = 'X'.
  MODIFY field_lst FROM field_cat TRANSPORTING checkbox
     WHERE fieldname EQ 'WI_CONFIRM' OR
           fieldname EQ 'WI_REJECT' OR
           fieldname EQ 'WI_DEADEX' OR
           fieldname EQ 'NOTE_EXIST' OR
           fieldname EQ 'ASYNCAGENT'.

  IF tcode = 'SWI2_ADM1'. " workitems ohne bearbeiter
    CLEAR field_cat.
    field_cat-col_pos = '0'.
    field_cat-key = 'X'.
    MODIFY field_lst FROM field_cat TRANSPORTING col_pos key
       WHERE fieldname EQ 'ASYNCAGENT'.
  ELSE.
    CLEAR field_cat.
    field_cat-tech = 'X'.
    MODIFY field_lst FROM field_cat TRANSPORTING tech
       WHERE fieldname EQ 'ASYNCAGENT'.
  ENDIF.

*- delete agents if necessary
  CALL FUNCTION 'SWP_ADMIN_DATA_READ' 
    IMPORTING
      wf_settings = wf_settings
    EXCEPTIONS
      OTHERS      = 1.
  IF wf_settings-no_agents = 'X'.
    field_cat-tech = 'X'.
    MODIFY field_lst FROM field_cat TRANSPORTING tech
       WHERE fieldname EQ 'EXEUSER' OR
             fieldname EQ 'FORW_BY'.
  ENDIF.

ENDFORM.                    "prepare_format

*---------------------------------------------------------------------*
*       FORM CALL_UCOMM_WILIST                                        *
*---------------------------------------------------------------------*
*       Dynamic call to process the keyboard input.                   *
*---------------------------------------------------------------------*
*  -->  UCOMM                                                         *
*  -->  SELFIELD                                                      *
*---------------------------------------------------------------------*

FORM call_ucomm_wilist   USING ucomm TYPE syucomm
                               selfield TYPE slis_selfield.
  DATA: old_list_cnt    LIKE g_list_cnt,
        s_return        LIKE swl_return,
        line_idx        LIKE sy-tabix,
        l_tabix         LIKE sy-tabix,
        linesel_cnt     LIKE sy-tabix,
        b_line_selected LIKE sy-binpt,
        ls_wiheader     TYPE LINE OF swfatalvitm.
  DATA: lt_wrkitm       TYPE swfatwrkitm.
  DATA: delta_list_cnt  TYPE sytabix.
  DATA: ls_por          TYPE sibflpor.
  DATA: lt_por          TYPE sibflport.
  DATA: lv_ucomm        TYPE syucomm.
  DATA: l_excp          TYPE REF TO cx_swf_ifs_exception.
  DATA: ls_suspend      TYPE swp_suspen.
  DATA: ls_swwwidh      TYPE swwwidh.
  DATA: l_wi_index      TYPE sytabix.
  DATA: lh_grid         TYPE REF TO cl_gui_alv_grid.
  DATA: l_grid_title    TYPE lvc_title.
  DATA: l_count         TYPE sytabix.


  PERFORM pick_line USING    selfield-tabindex
                             zlt_wiheader
                    CHANGING line_idx
                             linesel_cnt
                             b_line_selected.

  LOOP AT zlt_wiheader INTO ls_wiheader WHERE b_marked EQ 'X'.
    ls_por-catid  = swfco_objtype_bc.
    ls_por-instid = ls_wiheader-wi_id.
    APPEND ls_por TO lt_por.
  ENDLOOP.

  IF lt_por[] IS INITIAL.
    READ TABLE zlt_wiheader INDEX line_idx INTO ls_wiheader.
    ls_por-catid  = swfco_objtype_bc.
    ls_por-instid = ls_wiheader-wi_id.
    APPEND ls_por TO lt_por.
  ENDIF.

************************************************************************
*    Refresh Instancemanager                                         *
************************************************************************
  TRY.
      CALL METHOD cl_swf_run_wim_factory=>initialize( ).
    CATCH cx_swf_ifs_exception INTO l_excp.
      CALL METHOD cl_swf_utl_message=>send_message_via_exception( l_excp ).
  ENDTRY.

  CASE ucomm.
************************************************************************
*    Filter                                                            *
************************************************************************
    WHEN '&ILT'.

************************************************************************
*    Refresh                                                           *
************************************************************************
    WHEN '1REF'.
      CALL METHOD int->refresh
        IMPORTING
          ex_delta_count = delta_list_cnt.
      CALL METHOD int->get_entries
        IMPORTING
          ex_wientries = lt_wrkitm.
      CLEAR zlt_wiheader[].
      PERFORM convert_to_alv_list USING    lt_wrkitm
                                           p_more
                                  CHANGING zlt_wiheader.
      MESSAGE s811(w8) WITH delta_list_cnt.
      selfield-refresh    = 'X'.
      selfield-row_stable = 'X'.

************************************************************************
*   Pick                                                               *
************************************************************************
    WHEN '&IC1'.
      IF linesel_cnt > 1.
        MESSAGE s201(wi).
        EXIT.
      ENDIF.
      IF  line_idx     = 0.
        MESSAGE s004(0k).
        EXIT.
      ENDIF.

      READ TABLE zlt_wiheader INDEX line_idx INTO ls_wiheader.
      ls_por-catid  = 'BC'.
      ls_por-instid = ls_wiheader-wi_id.

      IF ls_wiheader-wi_type = wi_flow.
        lv_ucomm = function_wi_workflow_display.
      ELSE.
        lv_ucomm = cl_swf_rdg_dispatcher=>c_function_wi_display.
      ENDIF.
      CALL METHOD cl_swf_rdg_dispatcher=>execute_dialog_request
        EXPORTING
          im_por      = ls_por
          im_function = lv_ucomm
        EXCEPTIONS
          OTHERS      = 1.
      IF sy-subrc <> 0.
        MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                   WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
      ENDIF.

    WHEN OTHERS.

*      IF  line_idx     = 0.  "--- OSS note 1422569 ---
      IF linesel_cnt   = 0.
        MESSAGE s004(0k).
        EXIT.
      ENDIF.

      CALL METHOD cl_swf_rdg_dispatcher=>execute_dialog_request_multi 
        EXPORTING
          im_por      = lt_por
          im_function = ucomm
        EXCEPTIONS
          OTHERS      = 1.
      IF sy-subrc <> 0.
        MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                   WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
      ENDIF.

  ENDCASE.

  DATA: ls_layout TYPE slis_layout_alv.
  DATA: lt_filtered_entries TYPE  slis_t_filtered_entries.
  DATA: l_lines TYPE i.
  CALL FUNCTION 'REUSE_ALV_GRID_LAYOUT_INFO_GET'
    IMPORTING
      es_layout           = ls_layout
      et_filtered_entries = lt_filtered_entries
    EXCEPTIONS
      OTHERS              = 1.
  IF sy-subrc EQ 0.
    DESCRIBE TABLE lt_filtered_entries LINES l_lines.
    DESCRIBE TABLE zlt_wiheader LINES l_count.
    l_count = l_count - l_lines.
    PERFORM get_title USING l_count
                            g_windows_titlebar
                            l_grid_title.
    ls_layout-window_titlebar = l_grid_title.
    CALL FUNCTION 'REUSE_ALV_GRID_LAYOUT_INFO_SET'
      EXPORTING
        is_layout = ls_layout.
  ENDIF.

ENDFORM.                    "call_ucomm_wilist

*&---------------------------------------------------------------------*
*&      Form  set_sort_tab
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->P_IT_SORT  sorttab (ALV format)
*----------------------------------------------------------------------*
FORM set_sort_tab CHANGING p_it_sort TYPE slis_t_sortinfo_alv.
  DATA: is_sort TYPE slis_sortinfo_alv.

  REFRESH p_it_sort.
  is_sort-tabname = 'WIHEADER'.
  is_sort-spos = 1.
  is_sort-fieldname = 'WI_CD'.
  is_sort-down = 'X'.
  APPEND is_sort TO p_it_sort.

  is_sort-spos = 2.
  is_sort-fieldname = 'WI_CT'.
  is_sort-down = 'X'.
  APPEND is_sort TO p_it_sort.

  is_sort-spos = 3.
  is_sort-fieldname = 'WI_ID'.
  is_sort-down = 'X'.
  APPEND is_sort TO p_it_sort.

ENDFORM.                               " set_sort_tab
*&---------------------------------------------------------------------*
*&      Form  get_workitem_header
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->P_WI_HEADER  text
*----------------------------------------------------------------------*
FORM get_workitem_header USING    p_more      TYPE xfeld
                                  p_maxsel    TYPE tbmaxsel
                         CHANGING p_wi_header TYPE swfatalvitm.

  DATA: rangetab_for_id TYPE swfartwiid.
  DATA: rangetab_for_type TYPE swfartwitp.
  DATA: rangetab_for_creation_date TYPE swfartcrdat.
  DATA: rangetab_for_creation_time TYPE swfartcrtim.
  DATA: rangetab_for_task TYPE swfartrhtsk.
  DATA: rangetab_for_state TYPE swfartwista.
  DATA: rangetab_for_priority TYPE swfartprio.
  DATA: rangetab_for_dhsta TYPE swfartdhsta.
  DATA: lt_wrkitm TYPE swfatwrkitm.
  DATA: ls_wrkitm TYPE LINE OF swfatwrkitm.
  DATA: ls_alvitm TYPE LINE OF swfatalvitm.

  IF int IS INITIAL.
    int = cl_swf_rep_manager=>get_instance( ).
  ENDIF.

*- set selection properties
  IF p_more EQ 'X'.
    CALL METHOD int->set_property
      EXPORTING
        im_name  = if_swf_rep_workitem_selection=>c_get_administrator
        im_value = 'X'.
  ENDIF.

*- convert parameters
  rangetab_for_id[] = id[].
  rangetab_for_type[] = type[].
  rangetab_for_creation_date[] = cd[].
  rangetab_for_creation_time[] = ct[].
  rangetab_for_task[] = task[].
  rangetab_for_state[] = state[].
  rangetab_for_priority[] = prio[].
  rangetab_for_dhsta[] = dhsta[].

  CALL METHOD int->clear( ).
  CALL METHOD int->set_filter_strategy( filter ).

  CALL METHOD int->set_range_tab( rangetab_for_id ).
  CALL METHOD int->set_range_tab( rangetab_for_type ).
  CALL METHOD int->set_range_tab( rangetab_for_creation_date ).
  CALL METHOD int->set_range_tab( rangetab_for_creation_time ).
  CALL METHOD int->set_range_tab( rangetab_for_task ).
  CALL METHOD int->set_range_tab( rangetab_for_state ).
  CALL METHOD int->set_range_tab( rangetab_for_priority ).
  CALL METHOD int->set_range_tab( rangetab_for_dhsta ).
  CALL METHOD int->set_only_top_wi( top_only ).

  CALL METHOD int->set_maxsel( p_maxsel ).

  CALL METHOD int->read( ).

  CALL METHOD int->get_entries
    IMPORTING
      ex_wientries = lt_wrkitm.

  PERFORM convert_to_alv_list USING    lt_wrkitm
                                       p_more
                              CHANGING p_wi_header.


ENDFORM.                               " get_workitem_header

*---------------------------------------------------------------------*
*       FORM convert_to_alv_list                                      *
*---------------------------------------------------------------------*
*       ........                                                      *
*---------------------------------------------------------------------*
*  -->  WIHEADER                                                      *
*  -->  WIALVITM                                                      *
*---------------------------------------------------------------------*
FORM convert_to_alv_list USING    wiheader TYPE swfatwrkitm
                                  more     TYPE xfeld
                         CHANGING wialvitm TYPE swfatalvitm.
  DATA: ls_wrkitm TYPE LINE OF swfatwrkitm.
  DATA: ls_alvitm TYPE LINE OF swfatalvitm.
  DATA: lv_wi_handle TYPE REF TO if_swf_run_wim_internal.
  DATA: ls_admin TYPE swhactor.
  DATA: lt_agents TYPE tswhactor.

  LOOP AT wiheader INTO ls_wrkitm.                  
    MOVE-CORRESPONDING ls_wrkitm TO ls_alvitm.
    IF ls_wrkitm-wlc_flags O swfcr_p_asynchronous_rule.
      ls_alvitm-asyncagent = 'X'.
    ENDIF.
    IF more EQ 'X' AND ls_alvitm-wi_type EQ swfco_wi_flow.
      TRY.
          CALL METHOD cl_swf_run_wim_factory=>find_by_wiid 
            EXPORTING
              im_wiid     = ls_wrkitm-wi_id
            RECEIVING
              re_instance = lv_wi_handle.
          lt_agents = lv_wi_handle->get_administrator_agents( ).
          READ TABLE lt_agents INDEX 1 INTO ls_admin.
          ls_alvitm-adm_agent = ls_admin.
        CATCH cx_swf_run_wim.
      ENDTRY.
    ENDIF.
    IF top_only IS INITIAL.
      APPEND ls_alvitm TO wialvitm.
    ELSE.
      IF ls_alvitm-wi_chckwi IS INITIAL.
        APPEND ls_alvitm TO wialvitm.
      ENDIF.
    ENDIF.
  ENDLOOP.

ENDFORM.                    "convert_to_alv_list

*---------------------------------------------------------------------*
*       FORM PICK_LINE                                                *
*---------------------------------------------------------------------*
*       ........                                                      *
*---------------------------------------------------------------------*
*  -->  SELFIELD_IDX                                                  *
*  -->  WIHEADER                                                      *
*  -->  INDEX                                                         *
*  -->  LINESEL_CNT                                                   *
*  -->  B_OK                                                          *
*---------------------------------------------------------------------*
FORM pick_line USING    selfield_idx LIKE sy-tabix 
                            wiheader     TYPE swfatalvitm
                   CHANGING index        LIKE sy-tabix
                            linesel_cnt  LIKE sy-tabix
                            b_ok         LIKE sy-binpt.
  DATA: lines_marked LIKE sy-tabix,
        marked_idx   LIKE sy-tabix,
        cursor_line  LIKE sy-binpt.

  IF selfield_idx > 0.
    READ TABLE wiheader INDEX selfield_idx TRANSPORTING NO FIELDS.
    IF sy-subrc = 0.
      cursor_line = 'X'.
    ENDIF.
  ENDIF.

  LOOP AT wiheader TRANSPORTING NO FIELDS WHERE b_marked = 'X'.
    ADD 1 TO lines_marked.
    marked_idx  = sy-tabix.
  ENDLOOP.

************************************************************************
*  List tool algorithm                                                 *
************************************************************************
  IF cursor_line = 'X'.
    index = selfield_idx.
    IF lines_marked < 2.
      linesel_cnt = 1.
    ELSE.
      linesel_cnt = lines_marked.
    ENDIF.
    b_ok = 'X'.

  ELSEIF lines_marked = 1.
    index = marked_idx.
    linesel_cnt = 1.
    b_ok = 'X'.

  ELSEIF lines_marked > 1.
    linesel_cnt = lines_marked.
    b_ok = 'X'.

  ENDIF.

ENDFORM.                    "pick_line
*&---------------------------------------------------------------------*
*&      Form  GET_TITLE
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->P_HITS  text
*      -->P_L_STRING  text
*----------------------------------------------------------------------*
FORM get_title  USING p_hits TYPE sytabix
                      p_title_template TYPE string
                      p_title TYPE lvc_title.
  DATA: l_string TYPE string.
  DATA: l_count(10) TYPE n.

  IF p_hits > 1.
    l_string = '(&1 entries)'(014).
  ELSEIF p_hits EQ 1.
    l_string = '(1 entry)'(015).
  ENDIF.
  l_count = p_hits.
  SHIFT l_count LEFT DELETING LEADING '0'.
  CONCATENATE p_title_template l_string INTO p_title SEPARATED BY space.
  REPLACE '&1' IN p_title WITH l_count.

ENDFORM.                    " GET_TITLE
*&---------------------------------------------------------------------*
*&      Form  CANCEL_WORKITEM
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->P_ZLT_WIHEADER  text
*----------------------------------------------------------------------*
FORM cancel_workitem  TABLES p_zlt_wiheader TYPE swfatalvitm.
  TYPES: zlty_status TYPE RANGE OF sww_wistat.
  DATA: zlt_r_status TYPE RANGE OF sww_wistat.

  CONSTANTS: zlc_stat_complete TYPE sww_statxt VALUE 'COMPLETED',
             zlc_stat_cancel   TYPE sww_statxt VALUE 'CANCELLED',
             zlc_sign          TYPE  ddsign VALUE 'I',
             zlc_options       TYPE ddoption VALUE 'EQ'.

  zlt_r_status = VALUE zlty_status(
                       LET s = zlc_sign
                           o = zlc_options
                           IN sign   = s
                              option = o
                              ( low = zlc_stat_complete )
                              ( low = zlc_stat_cancel ) ).
*//Keep only the work items which has to be CANCELLED
  DELETE p_zlt_wiheader WHERE wi_stat IN zlt_r_status.

  IF p_zlt_wiheader[] IS NOT INITIAL.
    LOOP AT p_zlt_wiheader ASSIGNING FIELD-SYMBOL(<zlfs_wiheader>). 
      TRY.
          CALL FUNCTION 'SWW_WI_ADMIN_CANCEL' 
            EXPORTING
              wi_id                       = <zlfs_wiheader>-wi_id
              do_commit                   = abap_true
            IMPORTING
              new_status                  = <zlfs_wiheader>-wi_stat
            EXCEPTIONS
              update_failed               = 1
              no_authorization            = 2
              infeasible_state_transition = 3
              OTHERS                      = 4.
*- exception handling
        CATCH cx_swf_run_wim INTO DATA(lv_excp).
          DATA: zlr_txmgr TYPE REF TO cl_swf_run_transaction_manager.
          CALL METHOD zlr_txmgr->rollback( ). 
      ENDTRY.
    ENDLOOP.
  ENDIF.
ENDFORM.

add parameter

call SWW_WI_ADMIN_CANCEL in loop

Technical clean up for Solution Manager

This blog will focus on technical clean up for SAP solution manager.

Questions that will be answered are:

  • How can I reduce size of fast growing tables in SAP solution manager?
  • Which functions of SAP solution manager are in use or were in use?

SAP Readiness check for Cloud ALM

SAP Cloud ALM will replace SAP solution manager. You can run the SAP Readiness check for Cloud ALM to determine which functions of solution manager you use or were in use in the past.

Technical clean up general

SAP solution manager is a netweaver system which also used BI functions. For the general tables read the Technical clean up blog and for the BI parts, the BI clean up blog.

Solution manager specific clean up

A good starting point for clean up is OSS note 2549260 – Certain tables in Solution Manager system grow fast. This covers the most common use cases.

Special use cases are list below.

BI clean up

For BI specific clean up of solution manager, read OSS note2053261 – Solution Manager: HANA Database Cubes are not reorganized.

Custom code management clean up

Check OSS note 2790429 – Custom Code Management: Deletion Report for SP05 and higher. Then run program RAGS_CC_DATA_DELETION.

LMDB clean up

Table LMDB_P_CHANGELOG can grow very large. Note 1695927 – Cleaning up the change history in the LMDB explains to run program RLMDB_CLEAR_CHANGELOG for clean up.

CUP data

If table CUP_USAGE_DATA grows fast, follow the instructions from OSS note 2150720 – CUP enabled by default with automated cleanup process for outdated statistics.

E2E diagnostics data

If table SMD_HASH_TABLE is growing fast, check OSS note 1480588 – ST: E2E Diagnostics – BI Housekeeping – Information to clean up the data.

Technical monitoring data

If you are using Solution manager to do technical monitoring, the instructions for data reduction are listed in this OSS note: 2345041 – How to Reduce and Maintain Table Growth in Technical Monitoring for Solution Manger 7.1 & 7.2.

If you set up the monitoring in the past, but are not using it, best to switch it off. If you are actively using it, consider moving it to SAP Focused Run, which is far superior. More on Focused Run in this blog series.

Large SWJ_CONT table

If table SWJ_CONT is growing, you can run program RSPPF_SWJCLEAN for clean up. See note 1890845 – Removal of unnecessary entries from SWJ_CONT.

Technical clean up for BI

SAP BI can be used in BI itself, but BI is more available than you think: embedded BI in S4HANA, inside SCM, inside SAP solution manager, etc.

Also for BI systems a technical clean up of data might be required when the data volume becomes too high. For other clean up read this blog on technical clean up.

Questions that will be answered in this blog are:

  • How can I do a housekeeping on my BI system using a task list?
  • How can I execute a technical clean up of old BI technical data?

BI housekeeping task list

Go to transaction STC01 and start the SAP_BW_HOUSEKEEPING task list. Select all cleanups you want to perform. Select in the variants the retention times and dates. When done, start the task list (best to do in background mode):

When done all should be green.

Reference OSS note for this task list explaining all details steps: 1829728 – BW Housekeeping Task List.

Bug fix note for BI on HANA: 3016692 – SAP_BW_HOUSEKEEPING Tasklist issue of RSO_PSA_PARTITION_CHECK ended in checkmode.

Clean up RSDDSTATDTP

For table RSDDSTATDTP, you can use clean up program RSDDSTAT_DATA_DELETE (transaction RSDDSTAT):

Background OSS note: 2971233 – RSDDSTAT_DATA_DELETE: “free date selection” vs. “all”.

Other notes:

Clean up RSBATCHDATA and RSBATCHCTRL

For Clean up of tables RSBATCHDATA and RSBATCHCTRL, you can use program RSBATCH_DEL_MSG_PARM_DTPTEMP (transaction RSBATCH):

Parameters: DEL_MSG to delete all records older than XXX (From 000 to 999) days (M-Records). DEL_PAR to delete all records older than XXX (From 000 to 999) days (R and P-Records). DEL_DTP has no meaning.

Background: OSS note 1942703 – RSBATCH_DEL_MSG_PARM_DTPTEMP does not delete all expected RSBATCH* entries.

Other OSS notes:

Clean up of RSPM tables

Request administrations is stored in tables starting with RSPM. After years clean up will be needed for these tables to avoid them from growing. Main OSS note 3137171 – [BW Central KBA] Housekeeping for Request Administration tables (RSPM* tables) describes the process and programs.

SAP database growth control: technical cleanup

This blog will explain about technical cleanup to reduce the SAP database growth and to regain control of it.

Questions that will be answered are:

  • How to run the standard SAP clean up jobs?
  • Where can I find full list of items that could be cleaned up?
  • How to run the cleanup of some common objects?
  • Database reorganization after cleanup?
  • How can I clean up old idocs?
  • How can I clean up old table logging?
  • How can I clean up old application logs?
  • How can I clean up old RFC logs?
  • How can I clean up old change pointers?
  • How can I delete workflow logging?
  • How can I archive workflows?
  • How can I delete SAP office documents?
  • How can I delete old audit log data?
  • How can I execute specific clean up for BI systems?
  • How can I execute specific clean up for solution manager system?
  • Many more…. use search for table name

This blog assumes you have followed the step in the blog to get insight into your fast growing SAP tables.

If you run ECC on HANA or S4SHANA check out this blog on data aging.

This blog focuses on technical data objects archiving and clean up by performing deletion. If you want to setup functional archiving, start reading this blog.

List of technical clean up items

A full list of all possible technical clean up items can be found in OSS note 2388483 – How-To: Data Management for Technical Tables. The chapters below describe the most common ones.

SAP standard clean up jobs

Using SM36 you can plan all SAP standard jobs (which include a lot of clean up jobs for spools, dumps, etc) via the button Standard Jobs.

By hitting the button Default scheduling in an initial system, or after any upgrade or support package, the system will plan its default clean up schedule.

SM36 standard job scheduling

S4HANA has different set up of standard jobs. See blog.

Clean up of old idocs

Idoc data is stored in EDI* tables. Largest tables are usually EDI40, EDIDS and EDIDC.

Old idocs can be deleted using transaction WE11.

Idoc deletion

In batch mode you can schedule it as program RSETESTD.

In the bottom of the selection screen are the technical options:

Idoc deletion technical settings

The idoc deletion job can fail if there is too many data to process. If they happens remove the 4 tick boxes here and use the separate deletion programs: RSWWWIDE, RSARFCER, SBAL_DELETE and RSRLDREL2. These 5 combined programs will delete the same, but run more efficiently. This procedure is also explained in OSS note 1574016 – Deleting idocs with WE11/ RSETESTD.

Also check these OSS notes:

Clean up of table logging

Table logging is stored in table DBTABLOG (general information on table logging can be found in this blog). Deletion can be done using transaction SCU3 and then choosing the option Edit/Logs/Delete, or by using program RSTBPDEL.

After you apply OSS note 2535552 - SCU3: New authorization design for table logging: new transaction code SCU3_DEL will be available.

DBTABLOG deletion

More background information: OSS note 2335014 – DBTABLOG | Reduce size. Instructions to set up periodic job: 2388295 – RSTBPDEL | Delete logs periodically.

Bug fix OSS notes:

Clean up of application logging

Application logging (SLG1) is stored in tables BALDAT and BALHDR (for general information on the use of the application log, read this blog). Deletion can be done using transaction SLG2 or by using program SBAL_DELETE.

The last options to fine tune the number of logs per job and the commit counter setting do not appear by default. Select menu option Program/Expert mode first.

Read more details in the FAQ note: 3039724 – BALHDR and BALDAT: Application Log tables size increases [FAQ].

The deletion logic on expired and non-expired logs is described in OSS note 195157 – Application log: Deletion of logs.

For setting up a dynamic variant, follow the instructions in OSS note 2936391 – Dynamic variant to remove logs with SBAL_DELETE.

Tuned setting for commit counter is described in OSS note 2507213 – SBAL_DELETE runs too long.

Bug fix notes:

Delete old RFC data

Old RFC data can be deleted using transaction SM58, selecting some data, then in the overview screen select the menu option Log File/ Reorganize. Or by starting program RSARFCER.

More background information in OSS note 2899366 – Huge entries in table ARFCSDATA.

In this note you can also read to check SMQ1 as well, since qRFC’s are also stored in ARFCSDATA table. See blog on qRFC’s.

To delete records with update errors as well, run program RSTRFCES. See notes 3095792 – Unable to delete entries from SM58 transaction and 3245070 – How to delete tRFCs with error “Update terminated” in SM58.

Optimization and bug fix OSS notes:

Delete old change pointers

Old change pointers occupy space in tables BDCP2 and BDCPS. You can use transaction BD22 or report RBDCPCLR/RBDCPCLR2 (3248987 – The difference between reports RBDCPCLR and RBDCPCLR2) to delete them.

Delete change pointers

Detailed description of all the options can be found in OSS note 2676539 – BD22 (Report RBDCPCLR) Options Explained.

MDG change pointers

If you are using MDG: it has its own set of change pointer tables (MDGD_CP_REP_STAT). Clean up transaction code is MDGCPDEL. Program for batch job clean up is RMDGCPCLR.

Background in OSS note 3075612 – MDG-DRF: Reducing table entry of MDGD_CP_REP_STAT.

Bug fix note:

Workflows

Workflows are stored in many tables starting with SW*.

You can delete work item history with transaction SWWH or program RSWWHIDE.

Delete workflow item history

This clean up will only do the work item technical history and not the workflow itself. If workflow itself can be deleted or is to be archived is a functionality decision that the depend on the business and audit needs.

The workflow deleting program can create large amount of spools. If this is not wanted use the NULL printer.

If your business is using the GOS (generic object services) to see workflows linked to a business document, and they cannot retrieve the archived work item, please follow carefully the instructions in OSS note 2356250 – Not able to view archived workflows.

Workflow archiving

Workflow archiving can be done with archiving object WORKITEM (2578826 – Archiving Object WORKITEM – tables with deletion). For archiving setup read this blog. This note explains how to run the archiving of the WORKITEM object: 2157048 – Workflow Quick Start Guide to WORKITEM Archiving. Data display for the archived work items is explained in OSS note 2748817 – How to display Workitems from archive.

Only workflows of status Completed or Logically deleted (CANCELLED) can be archived (see OSS note 2311382 – Not all work items are archived). You can use transaction SWIA for mass logical deletion (see blog, and OSS note 2650820 – Mass complete work items manually). A custom program can be used for mass cancellation in batch mode (see blog).

If you run on HANA, read OSS note 3251001 – WORKITEM tables disk size not reduced after archiving.

Bug fix OSS notes:

Workflow deletion

If you want to delete the actual workflow you have to run program RSWWWIDE.

Take care that before deleting workflows you have checked that these are not needed for audit or financial proof. Some workflows will contain approval steps with a recording of who approved what at which time.

Orphaned workflow records

Run program RSWWWIDE_DEP to list and delete orphaned workflow records. See OSS notes 2971286 – Table SWW_PROPERTIES and 3144853 – SWWLOGHIST table size is increasing.

Large amount of documents in SAP inbox

If you have a large amount of items in your SAP inbox, you can delete them via program RSSODLIN. Background is in OSS note 63912 – SAPoffice: Delete user sessions.

Deleting SAP office documents

SAP office documents are stored in table SOFFCONT1, and can be deleted with program RSBCS_REORG. See note 966854 – Reorganization – new report. Note 988057 – Reorganization – information contains a very useful PDF document that explains what to do in cases that RSBCS_REORG is not directly can delete an SAP office document. In most cases you have to run a special program that breaks the link between the document and the data. After that is done you can delete the content. Extra explanation is in OSS note 1641830 – Reorganization of Business Communication Services data (RSBCS_REORG).

Test this first and check with the data owner that the documents are no longer needed.

For a full explanation on deleting SAP office documents (including all the pre-programs to run) and bug fix notes: read this dedicated blog on SAP office document deletion.

Migrating SAP office documents to content server.

Usually the business will not allow deletion of SAP office document (unless they are very old). You might be ending up with a SOFFCONT1 table of 100 GB or more.

In stead of deleting SAP office documents, you can also migrate them to a content server. Read more in this blog.

Change documents

Change documents do contain business data changes to business objects. If tables CDHDR and CDPOS grow very big, you start with an age analysis. You can propose to business to delete change documents older than 10 years. 10 years is the legal time you need to keep a lot of data. Deletion is done via program RSCDOK99. If business does not want to delete, but keep the data in the archive, you can use data archiving object CHANGEDOCU. Retrieval of archived change documents is via transaction RSSCD100.

Background in OSS note: 3103201 – CD: Archivierungsmöglichkeiten für die CDPOS.

Read this extensive blog on Change document data archiving.

Bug fix OSS notes:

Large SMIMCONT1

See OSS note 3171193 – SMIMCONT1 table size is large to run in SE37 the clean up function module CLEAN_LANG_SMIMCONT1. This only cleans obsolete entries.

SYS_LOB tables

If you have large SYS_LOB tables, most likely these are occupied with attachments. Consider setup of SAP content server (see blog) and then migrate the documents from the SAP database to the content server (see blog).

To analyze SYS_LOB tables, follow the instructions in this dedicated blog.

LTEX table

LTEX table is used for storing ALV extracts data. Use program BALVEXTR to delete old entries. See OSS note 557772 – ALV extracts: Improving the BALVEXTR management report.

Clean up old Audit log data

You can schedule program RSAUPURG or program RSAU_FILE_ADMIN with the right variant to delete old Audit log data:

Before deleting audit log data, first agree with your security officer on the retention period. More on audit log in this blog.

Clean up of user role assignment data

If you have an older system, you will find that many users will have double roles assigned, or roles with validity dates in the past. This will lead to large amount of entries in table AGR_USERS. You can clean up by compressing this data with program PRGN_COMPRESS_TIMES. Read more in this blog.

Clean up of web service data

If table SRT_MMASTER is growing fast, it is time for clean up of web service data: see OSS note 2231932 – ESI – How to schedule the SAP_SOAP_RUNTIME_MANAGEMENT standard background job.

Clean up of BI data

For clean up of BI data please read this dedicated blog on clean up of BI data.

In the system that BI system extracts data from, you can run diverse cleanups:

Clean up for solution manager system

For clean up of a solution manager system, read this dedicated blog.

Clean up for SAP Focused Run

For clean up of a SAP Focused Run system, read this dedicated blog.

Updating statistics

If you are running Oracle database it is wise to include in technical clean up job as last step the online reorganization of tables or indexes using program RSANAORA. See blog.

Clean up non-used indexes

Oracle has a function called index monitoring to check if indexes are used at all.  You can use it to delete non-used indexes. See OSS note 105047 – Support for Oracle functions in the SAP environment.