Skip to Content
Technical Articles
Author's profile photo Amit Diwane

SAP ABAP Programming Model for FIORI- List Report Application (Part 2)

Hello All,

This is the second blog in this series. You can refer to the first blog in the below link:

SAP ABAP Programming Model for FIORI- List Report Application (Part 1)

In this blog we will be looking into BOPF part to enable the Determination, Validation and Action in our Fiori List report Application:

Let’s proceed with this tutorial. In last blog we have created a Transaction CDS on SPFLI Table.

Go to the CDS View and click on the highlighted dot, and then the link for the Generated BOPF Object as shown in below image:

BOPF%20Path

BOPF Path

 

The Generated Business Object will look as below. Click on ‘Go to Root Node’:

 

In the root Node we can see option to Navigate for Determination, Action and Validation

BOPF%20Root%20Node

BOPF Root Node

 

Determination: It can be use to determine some value at runtime. In our case we are using it to generate the Primary key value at runtime.

To create a new Determination, click on the Determination link in above image and then click on ‘New’ Button.

 

 

Give the name, description and Category as given below. (Ignore the error, I am receiving it as I have already created it)

 

Once created click on ‘Triggers Configured’ and enable it for create and update. You can also see the generated class for this determination where we will be writing our code:

 

Now go into the generated class and write the below code:

class ZCL_IDEMO_D_CALCULATEKEYVALUES definition
  public
  inheriting from /BOBF/CL_LIB_D_SUPERCL_SIMPLE
  final
  create public .
public section.
  methods /BOBF/IF_FRW_DETERMINATION~EXECUTE
    redefinition .
protected section.
private section.
ENDCLASS.

CLASS ZCL_IDEMO_D_CALCULATEKEYVALUES IMPLEMENTATION.
  METHOD /bobf/if_frw_determination~execute.

    "Read data with the given keys (typed with combined type table)
    DATA lt_data TYPE ztidemo_ddl_spfli.
    "Call retrieve method of BOPF Framework
    io_read->retrieve(
      EXPORTING
        iv_node                 = is_ctx-node_key   " uuid of node name
        it_key                  = it_key            " keys given to the determination
      IMPORTING
        eo_message              = eo_message        " pass message object
        et_data                 = lt_data           " itab with node data
        et_failed_key           = et_failed_key     " pass failures
    ).
    IF eo_message IS NOT BOUND.
      eo_message = /bobf/cl_frw_factory=>get_message( ).
    ENDIF.

"Select Data from DB Table
    SELECT FROM spfli
      FIELDS carrid, MAX( connid ) AS max
      GROUP BY carrid
      INTO TABLE @DATA(lt_max_item).


    " Assign numbers to each newly created item and trigger the modification in BOPF
    LOOP AT lt_data REFERENCE INTO DATA(lr_data).
      IF lr_data->connid IS INITIAL.
"As the field is read only it will be always Initial in our case
        READ TABLE lt_max_item WITH KEY carrid = lr_data->parent_key REFERENCE INTO DATA(lr_max_item).
        IF sy-subrc = 0.
          lr_max_item->max = lr_max_item->max + 10.
        ELSE.
"Assign an Incremented value. In real case scenario we can get the new number from number range "function
          APPEND INITIAL LINE TO lt_max_item REFERENCE INTO lr_max_item.
          lr_max_item->max = lr_data->parent_key.
          lr_max_item->max = lt_max_item[ carrid = 'AA' ]-max + 1 .
          lr_max_item->carrid = 'AA'.
        ENDIF.

        " Fill leading zeros for ALPHANUM field on database
        lr_data->carrid = lr_max_item->carrid.
        lr_data->connid = |{ lr_max_item->max ALPHA = IN }|.
        lr_data->arrtime = sy-uzeit.
        lr_data->deptime = sy-uzeit.

        io_modify->update(
              EXPORTING
                iv_node           = is_ctx-node_key    " uuid of node
                iv_key            = lr_data->key       " key of line
                is_data           = lr_data            " ref to modified data
                it_changed_fields =
                      VALUE #( ( zif_idemo_ddl_spfli_c=>sc_node_attribute-zidemo_ddl_spfli-carrid )
                                                      (   zif_idemo_ddl_spfli_c=>sc_node_attribute-zidemo_ddl_spfli-connid )
                                                      (   zif_idemo_ddl_spfli_c=>sc_node_attribute-zidemo_ddl_spfli-arrtime )
                                                      (   zif_idemo_ddl_spfli_c=>sc_node_attribute-zidemo_ddl_spfli-deptime )
                                                      (   zif_idemo_ddl_spfli_c=>sc_node_attribute-zidemo_ddl_spfli-fltype )
                                                    )
        ). "Here you have to pass all the field name which you change or passing value
     ENDIF.
    ENDLOOP.
  ENDMETHOD.
ENDCLASS.

 

Similarly create a new Validation by clicking on the Validation link under the root node. You can refer below image for the generated Validation:

BOPF%20Validation

BOPF Validation

 

Validation: Validation are use to validate the Input Screen data.

In our case we are validating the Country From Field.

I have created a Message class with below messages-

Message%20Class

Message Class

Go to the generated validation class and write down the below code:

CLASS zcl_idemo_v_validatecountry DEFINITION
  PUBLIC
  INHERITING FROM /bobf/cl_lib_v_supercl_simple
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    METHODS /bobf/if_frw_validation~execute
        REDEFINITION .
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS ZCL_IDEMO_V_VALIDATECOUNTRY IMPLEMENTATION.
  METHOD /bobf/if_frw_validation~execute.
    " Internal tab for Header Data
    " Created using reference to Generated Table Type
    DATA(lt_head) = VALUE ztidemo_ddl_spfli( ).
    " Get Header Data row under process
    io_read->retrieve(
    EXPORTING
    iv_node = is_ctx-node_key " Node Name
    it_key = it_key " Key Table
    IMPORTING
    et_data = lt_head " Data Return Structure
    ).
    " Instantiate Message Object if not bound
    IF eo_message IS NOT BOUND.
      eo_message = /bobf/cl_frw_factory=>get_message( ).
    ENDIF.
    "Loop the fetched records
    LOOP AT lt_head REFERENCE INTO DATA(lr_head).
      " Validate
      DATA ls_msg TYPE symsg.
      "If Country From is blank raise an error
      IF lr_head->countryfr IS INITIAL.
        ls_msg-msgty = 'E'.
        ls_msg-msgid = 'ZBOPF'.
        ls_msg-msgno = '000'.
      ELSE.
        "If entered Country is not valid raise an error
        SELECT COUNT(*) FROM t005 INTO @DATA(lv_count) WHERE land1 = @lr_head->countryfr.
        IF  lv_count = 0.
          ls_msg-msgty = 'E'.
          ls_msg-msgid = 'ZBOPF'.
          ls_msg-msgno = '001'.
        ENDIF.
      ENDIF.

      " Add Message
      IF NOT ls_msg IS INITIAL.
        eo_message->add_message(
        EXPORTING
        is_msg = ls_msg " Message
        iv_node = is_ctx-node_key " Node
        iv_key = lr_head->key " Instance key
        ).

        "Below step is important to stop the flow when error occurs
        DATA ls_key TYPE /bobf/s_frw_key.
        ls_key-key = lr_head->key.

        APPEND ls_key TO et_failed_key.

      ENDIF.
    ENDLOOP.
  ENDMETHOD.
ENDCLASS.

 

In Case if Validation fails, the create or update process will not get complete and error will appear on the FIORI App screen as shown below:

Validation%20Errors

Validation Errors

Finally we will be adding the Action to our BOPF:

Action: Usually we use it to perform some activity based on some event which cannot be handled in create, update or delete methods.

In our case we are creating a new record by referring to the selected one. The Action button is added to the UI view with the help of annotation mentioned in the Metadata Extension view for SPFLI.

 

To create a Action we need to navigate to the root node for the generated Business Object of SPFLI (Shown in the image earlier) and click on the Action link.

Click on the ‘New’ Button in next page and create new Action referring to the below Image.(Note that the Action Name should match to the dataAction Value mentioned in the Metadata Extension view file).

 

After the Action is created go to the generated class and write down the below code:

CLASS zcl_idemo_a_copy DEFINITION
  PUBLIC
  INHERITING FROM /bobf/cl_lib_a_supercl_simple
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    METHODS /bobf/if_frw_action~execute
        REDEFINITION .
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS zcl_idemo_a_copy IMPLEMENTATION.

  METHOD /bobf/if_frw_action~execute.
    DATA: lr_head_ref TYPE REF TO data.

    DATA(lt_head) = VALUE ztidemo_ddl_spfli( ).
    DATA ls_head_ref TYPE zsidemo_ddl_spfli.

    " Retrieve the data from the Node
    io_read->retrieve(
    EXPORTING
    iv_node = is_ctx-node_key
    it_key = it_key
    IMPORTING
    et_data = lt_head
     ).

    "Copy the data from retrieved node
    LOOP AT lt_head REFERENCE INTO DATA(lr_head).
      ASSIGN lr_head->* TO FIELD-SYMBOL(<fs_head>).

      ls_head_ref-carrid = <fs_head>-carrid.
      "Increase the connection id by 10 to avoid the primary key violation error
      ls_head_ref-connid = <fs_head>-connid + 10.
      "Date and time cannot be kept blank else the OData Metadata will fail to load
      ls_head_ref-arrtime = sy-uzeit.
      ls_head_ref-deptime = sy-uzeit.
      ls_head_ref-countryfr = <fs_head>-countryfr.
      "Copy field data
      CREATE DATA lr_head_ref TYPE zsidemo_ddl_spfli.
      ASSIGN lr_head_ref->* TO FIELD-SYMBOL(<fs_head_ref>).
      IF <fs_head_ref> IS ASSIGNED.
        <fs_head_ref> = ls_head_ref.
      ENDIF.

      "Create Record
      io_modify->create(
            EXPORTING
                iv_node = is_ctx-node_key
                is_data = lr_head_ref
            IMPORTING
                ev_key = DATA(lv_copy_key)
            ).

    ENDLOOP.
  ENDMETHOD.
ENDCLASS.

 

So here we are completed with this blog. We have successfully added the Determination, Validation and Action Node behavior to our BOPF. In the next blog we will see how to finally consume this view as a Fiori Application using SAP WebIDE.

SAP ABAP Programming Model for FIORI- List Report Application (Part 3)

Thanks,

Amit Diwane

Assigned Tags

      12 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Christophe Bontron
      Christophe Bontron

      Hello,

      Very interesting and complete blog.

      Usually the creation of documents, items in SAP is done through Bapi, function module. Database table is never updated directly. It seems that with Bopf, the table is updated directly. Is there a way to use a Bapi?

      Thank you.

      Christophe.

      Author's profile photo Amit Diwane
      Amit Diwane
      Blog Post Author

      Hello Christophe,

      Thanks for your comment. Although I never tried calling the BAPI within BOPF but you can create an Action where you will read all the Header and Item data (retrieve it by association) and then call the BAPI.

      Thanks,

      Amit Diwane

      Author's profile photo Angshuman Chakraborty
      Angshuman Chakraborty

      Hi Amit, This was a nice blog indeed. I was able to call a BAPI in a Determination on Fiori List report Save action. The BAPI working fine and able to create a new record as expected.

      My issue is, I am unable to show the BOPF return message after the Object page "Save" action.  Do you have any insight on this? I raised the message using the eo_message parameter and I can see the message at the browser response (F12) when I do the "save" action. But its not just showing in the app.

       

      Thank

      Angshuman

      Author's profile photo Amit Diwane
      Amit Diwane
      Blog Post Author

      Hello Angshuman,

      Thanks letting us know that BAPI works with Determination. I tried passing the message in the determination class ZCL_IDEMO_D_CALCULATEKEYVALUES. The code is below for your reference. This code I added at the end of the method execute.

       

              DATA ls_msg TYPE symsg.
                ls_msg-msgty = ‘I’.
                ls_msg-msgid = ‘ZBOPF’.
                ls_msg-msgno = ‘002’. // New message created in message class
              eo_message->add_message(
              EXPORTING
              is_msg = ls_msg ” Message
              iv_node = is_ctx-node_key ” Node
              iv_key = lr_data->key ” Instance key
              ).
      I am able to see the message pass as a Message Toast in the UI screen after the entry gets created successfully. Just make sure all the mandatory and date time field getting pass back to screen should not be Initial.
      Hope this helps you.
      Regards,
      Amit Diwane
      Author's profile photo Sankar Roy
      Sankar Roy

      Hi Angshuman,

       

      If incase we use determination, do we need separately annotate the determination name in the CDS Consumption View?

       

      Can you please share your consumption view code?

      Author's profile photo Angshuman Chakraborty
      Angshuman Chakraborty

      Hi

      We don't need separate annotations for determination. The Object model annotation will create the BOPF. Inside BOPF you can create a determination class (either in eclipse or in transaction BOBX).

       

      The objectModel annotation the BOPF screenshot attached below.

      // BOPF Annotation in CDS
      @ObjectModel:{ modelCategory: #BUSINESS_OBJECT,
                     compositionRoot: true,
                     writeActivePersistence: 'ZPM_NOTIF_LIST',
                     transactionalProcessingEnabled: true,
                     representativeKey: 'QMNUM',
                     createEnabled: true     
                   }

      Click%20on%20New%20to%20create%20new%20determination%20Class

      Click on New to create New determination Class in eclipse

      Author's profile photo ragavie dakishna
      ragavie dakishna

      Very Nice blog

      Author's profile photo Carine Tchoutouo Djomo
      Carine Tchoutouo Djomo

      Hello Amit,

      Nice blog.

      Please note that the ABAP RESTful Application Programming Model (RAP) is the evolutionary successor of the BOPF-based ABAP Programming Model for SAP Fiori.

      More information can be found here:

      Kind regards,
      Carine

      Author's profile photo Rakesh Gowdru
      Rakesh Gowdru

      Hi Amit,

      Nice Blog.

      I followed steps can able to see Action button but when i tried to trigger action by selecting the record respective action class is not triggering but when i tested from Generated Business Object the class is triggering is anything missed.

      Thanks.

      Author's profile photo Amit Diwane
      Amit Diwane
      Blog Post Author

      Hello @Rakesh Gowdru,

       

      I guess you might have missed the annotations on the CDS end. Also make sure the dataAction name matches with BOPF method name.

      Author's profile photo Björn Kron
      Björn Kron

      It is really a great article and helped me a lot. I just started to work with CDS views and I was able to reproduce and understand everything what was explained here. I am just asking myself if it would be possible to mark the fields which are not filled or not correctly filled in red and set error message beside this fields.

      Author's profile photo Amit Diwane
      Amit Diwane
      Blog Post Author

      Hello Björn Kron ,

       

      Glad to know this blog series helped you. Regarding your query you can mark the field as Mandatory in the CDS Metadata.

       

      Hope this helps.

       

      Regards,

      Amit Diwane