Passing filter to object page table – ABAP RAP Custom Entity

This blog explains how to apply user filter to not only list page but also in object page, especially to restrict data in the associated tables in object page.

Prerequisite:

The exercise application from my previous blog will be used. In this blog, we will extend this simple RAP application with multiple tables in object page. Please makes sure to complete the exersice in this previous blog before starting.

Multiple Facets in ABAP RAP Object Page with Custom Entity – SAP Extensibility 101

The setup

In the previous blog, we have created sales order list and in the object page we can view the sales order item data and scheduling line data.

Now let’s say user only wants to see all the sales order and scheduling line before certain date in the past. To do that, in this blog, we will add a filter ‘Key Date’. If user enters 2020.01.01, only sales order with confirmed delivery date before that should be displayed.

1. Adding filter field to root entity

In the root entity ZSALESDOC_C, add field ‘KEYDATE’ with date type. The most important part is that it has to be a key field to make the value pass down to the object page.

  • UI.selectionField will make it display as user filter
  • Hide it from list item as it’s not needed to show in the output
  • Add label
  • Key date will be single value parameter
define root custom entity ZSALESDOC_C
{
      @UI.facet : [ { id: 'SalesDoc',
                      purpose: #STANDARD,
                      type  : #IDENTIFICATION_REFERENCE,
                      label : 'Sales Document',
                      position: 10 },
                    { id    :            'LineItem',
                      purpose  :       #STANDARD,
                      type  :          #LINEITEM_REFERENCE,
                      label :         'Sales Doc Item',
                      position :      20,
                      targetElement: '_LineItem'},
                    { id    :            'ScheLine',
                      purpose  :       #STANDARD,
                      type  :          #LINEITEM_REFERENCE,
                      label :         'Schedule Line Item',
                      position :      30,
                      targetElement: '_ScheLine'}
                      ]
      @UI.lineItem          : [{ position: 10 }]
      @UI.selectionField    : [{position: 10}]
      @UI.identification    : [{position: 10}]
  key VBELN     : vbeln_va;
      @UI.selectionField    : [{position: 20}]
      @UI.lineItem          : [{ hidden: true }]
      @EndUserText.label    : 'Key date'
      @Consumption.filter:{ selectionType: #SINGLE}
  key KEYDATE   : abap.dats; // Used for filtering the Creation date and dates in object page. Needs to be key field to be passed to object page ABAP class
      @UI.lineItem          : [{ position: 20 }]
      @UI.identification    : [{position: 20}]
      KUNNR     : kunag;
      @UI.lineItem          : [{ position: 30 }]
      @UI.identification    : [{position: 30}]
      AUART     : auart;
      @UI.lineItem          : [{ position: 40 }]
      @UI.identification    : [{position: 40}]
      ERDAT     : erdat;


      //Associations
      _LineItem : composition [0..*] of ZSALESITEM_C;
      _ScheLine : composition [0..*] of ZSALESSCHEDULE_C;
}

2. Applying filter to list page

Now to apply the filter to the output of list page, we need to do that in the ABAP class in root entity ZCL_SALESDOC.

  • As KEYDATE is a key field in root entity, the value entered by user is stored in lt_conditions_range. Fetch the value of the first row as it is a single parameter.
  • If the fitler is left empty, use current system date instead.
  • Apply the key date to erdat(sales order creation date) getting only the sales order created before the key date.
CLASS zcl_salesdoc IMPLEMENTATION.
  METHOD if_rap_query_provider~select.

    DATA: lt_r_vbeln   TYPE tt_range_option,
          lt_r_keydate TYPE tt_range_option,
          lv_keydate   TYPE dats,
          lv_count     TYPE int8.

    IF io_request->is_data_requested( ).

      DATA(lv_parameter)     = io_request->get_parameters( ).

      DATA(lv_top)     = io_request->get_paging( )->get_page_size( ).
      IF lv_top < 0.
        lv_top = 1.
      ENDIF.

      DATA(lv_skip)    = io_request->get_paging( )->get_offset( ).

      DATA(lt_sort)    = io_request->get_sort_elements( ).

      DATA : lv_orderby TYPE string.
      LOOP AT lt_sort INTO DATA(ls_sort).
        IF ls_sort-descending = abap_true.
          lv_orderby = |'{ lv_orderby } { ls_sort-element_name } DESCENDING '|.
        ELSE.
          lv_orderby = |'{ lv_orderby } { ls_sort-element_name } ASCENDING '|.
        ENDIF.
      ENDLOOP.
      IF lv_orderby IS INITIAL.
        lv_orderby = 'vbak~vbeln'.
      ENDIF.
*
*          DATA(lv_conditions) =  io_request->get_filter( )->get_as_sql_string( ).
      DATA(lt_conditions_range) =  io_request->get_filter( )->get_as_ranges( ).

      IF line_exists( lt_conditions_range[ name = 'VBELN' ] ).
        lt_r_vbeln  = lt_conditions_range[ name = 'VBELN' ]-range.
      ENDIF.

      IF line_exists( lt_conditions_range[ name = 'KEYDATE' ] ).
        lt_r_keydate  = lt_conditions_range[ name = 'KEYDATE' ]-range.
        READ TABLE lt_r_keydate ASSIGNING FIELD-SYMBOL(<lw_r_keydate>) INDEX 1.
        lv_keydate = <lw_r_keydate>-low.
      ELSE.
        lv_keydate = sy-datum. "Todays date if no filter value is entered
      ENDIF.

      SELECT DISTINCT
             vbak~vbeln,
             vbak~erdat,
             vbak~auart,
             vbak~kunnr,
             @lv_keydate AS keydate "Pass the filter value to object page
        FROM vbak
       INNER JOIN kna1
          ON vbak~kunnr = kna1~kunnr
       INNER JOIN vbap
          ON vbap~vbeln = vbak~vbeln
       WHERE vbak~vbeln IN @lt_r_vbeln
         AND vbak~erdat <= @lv_keydate  "Get all data before the key date
       ORDER BY (lv_orderby)
       INTO TABLE @DATA(lt_vbak)
       UP TO @lv_top ROWS OFFSET @lv_skip.

      "Get the total number of records
      SELECT DISTINCT
             vbak~vbeln,
             vbak~erdat,
             vbak~auart,
             vbak~kunnr,
             @lv_keydate AS keydate "Pass the filter value to object page
        FROM vbak
       INNER JOIN kna1
          ON vbak~kunnr = kna1~kunnr
       INNER JOIN vbap
          ON vbap~vbeln = vbak~vbeln
       WHERE vbak~vbeln IN @lt_r_vbeln
         AND vbak~erdat <= @lv_keydate  "Get all data before the key date
       ORDER BY (lv_orderby)
       INTO TABLE @DATA(lt_vbak_count).

      lv_count = lines( lt_vbak_count ).

      io_response->set_total_number_of_records( lv_count ). "Set the max total count of rows expected in the list report.
      io_response->set_data( lt_vbak ).

    ENDIF.
  ENDMETHOD.

3. Adding filter field to child entity

Now the filter field KEYDATE must be added to the child entity ZSALESSCHEDULE_C, as it’s the Delivery Date in scheduling line entity that we want to restrict with KEYDATE.

  • Add the same field name and same type
  • Does not have to be key field in the child entity as the value doesn’t need to be passed from the child entity to anywhere else.
  • Hide from output
  • Add KETDATE to join condition to root entity
define custom entity ZSALESSCHEDULE_C
{
      @UI.facet : [ { id:            'LineItem',
                     purpose   :       #STANDARD,
                     type      :          #IDENTIFICATION_REFERENCE,
                     label     :         'Schedule Line Items',
                     position  :      10 } ]
      @UI.hidden: true
  key vbeln     : vbeln_va;
      @UI.lineItem          : [{ position: 10 }]
      @UI.identification    : [{position: 10}]
  key posnr     : posnr_va;
      @UI.lineItem          : [{ position: 20 }]
      @UI.identification    : [{position: 20}]
  key etenr     : etenr;
      @UI.lineItem          : [{ position: 30 }]
      @UI.identification    : [{position: 30}]
      edatu     : edatu;
      @UI.lineItem          : [{ position: 40 }]
      @UI.identification    : [{position: 40}]
      @Semantics: { quantity : {unitOfMeasure: 'vrkme' } }
      wmeng     : wmeng;
      @UI.lineItem          : [{ position: 50 }]
      @UI.identification    : [{position: 50}]
      @Semantics: { quantity : {unitOfMeasure: 'vrkme' } }
      bmeng     : bmeng;
      @UI.lineItem          : [{ position: 60 }]
      @UI.identification    : [{position: 60}]
      vrkme     : vrkme;
      @UI.hidden: true  
      keydate : abap.dats; // Used for filtering the object page. Does not need to be key field here in object page

      _SalesDoc : association to parent ZSALESDOC_C on  $projection.vbeln     = _SalesDoc.VBELN
                                                    and $projection.keydate = _SalesDoc.KEYDATE;
}

4. Applying filter to object page

Now to apply the filter to the object page output of sales order scheduling line, we need to do that in the ABAP class in root entity ZCL_SALESSCHEDULE.

The logic is the same as in ZCL_SALESDOC except it’s applied to delivery date in the scheduling line. It’s also not required in the SQL output and in returning date.

CLASS zcl_salesschedule IMPLEMENTATION.
  METHOD if_rap_query_provider~select.

    DATA: lt_r_vbeln   TYPE tt_range_option,
          lt_r_keydate TYPE tt_range_option,
          lv_keydate   TYPE dats,
          lv_count     TYPE int8.

    IF io_request->is_data_requested( ).

      DATA(lv_parameter)     = io_request->get_parameters( ).

      DATA(lv_top)     = io_request->get_paging( )->get_page_size( ).
      IF lv_top < 0.
        lv_top = 1.
      ENDIF.

      DATA(lv_skip)    = io_request->get_paging( )->get_offset( ).

      DATA(lt_sort)    = io_request->get_sort_elements( ).

      DATA : lv_orderby TYPE string.
      LOOP AT lt_sort INTO DATA(ls_sort).
        IF ls_sort-descending = abap_true.
          lv_orderby = |'{ lv_orderby } { ls_sort-element_name } DESCENDING '|.
        ELSE.
          lv_orderby = |'{ lv_orderby } { ls_sort-element_name } ASCENDING '|.
        ENDIF.
      ENDLOOP.
      IF lv_orderby IS INITIAL.
        lv_orderby = 'vbap~vbeln, vbap~posnr, vbep~etenr'.
      ENDIF.
*
*          DATA(lv_conditions) =  io_request->get_filter( )->get_as_sql_string( ).
      DATA(lt_conditions_range) =  io_request->get_filter( )->get_as_ranges( ).

      IF line_exists( lt_conditions_range[ name = 'VBELN' ] ).
        lt_r_vbeln  = lt_conditions_range[ name = 'VBELN' ]-range.
      ENDIF.

      IF line_exists( lt_conditions_range[ name = 'KEYDATE' ] ).
        lt_r_keydate  = lt_conditions_range[ name = 'KEYDATE' ]-range.
        READ TABLE lt_r_keydate ASSIGNING FIELD-SYMBOL(<lw_r_keydate>) INDEX 1.
        lv_keydate = <lw_r_keydate>-low.
      ELSE.
        lv_keydate = sy-datum. "Todays date if no filter value is entered
      ENDIF.

      SELECT
        FROM vbap
       INNER JOIN vbep
          ON vbap~vbeln = vbep~vbeln
         AND vbap~posnr = vbep~posnr
      FIELDS vbap~vbeln,
             vbap~posnr,
             vbep~etenr,
             vbep~edatu,
             vbep~wmeng,
             vbep~bmeng,
             vbep~vrkme
       WHERE vbap~vbeln IN @lt_r_vbeln
         AND vbep~edatu <= @lv_keydate  "Get all data before the key date
       ORDER BY (lv_orderby)
       INTO TABLE @DATA(lt_vbep)
       UP TO @lv_top ROWS OFFSET @lv_skip.

      "Get the total number of records
      SELECT
        FROM vbap
       INNER JOIN vbep
          ON vbap~vbeln = vbep~vbeln
         AND vbap~posnr = vbep~posnr
      FIELDS vbap~vbeln,
             vbap~posnr,
             vbep~etenr,
             vbep~edatu,
             vbep~wmeng,
             vbep~bmeng,
             vbep~vrkme
       WHERE vbap~vbeln IN @lt_r_vbeln
         AND vbep~edatu <= @lv_keydate  "Get all data before the key date
       ORDER BY (lv_orderby)
       INTO TABLE @DATA(lt_vbep_count).

      IF io_request->is_data_requested( ).
        io_response->set_data( lt_vbep ).
      ENDIF.

      IF io_request->is_total_numb_of_rec_requested(  ).
        io_response->set_total_number_of_records( lines( lt_vbep_count ) ).
      ENDIF.

    ENDIF.
  ENDMETHOD.

5. App preview

Now preview the app and you can see the Key Date as a filter. Let’s test with sales order 6 with creation date Nov 20, 2015 and three dcheduling lines with delivery dates “Nov 20, 2015”, “Apr 25, 2022” and “Dec 4, 2015”.

Now let’s enter a key date “Nov 20, 2015” in the filter. You can see that sales order 6 is in the list page output.

Click on sales order 6 and in the object page, you can see the scheduling line is restricted with the key date as well.

One Reply to “Passing filter to object page table – ABAP RAP Custom Entity”

Comments are closed.