Skip to Content
Technical Articles
Author's profile photo Andre Fischer

How to check mandatory fields in RAP

Introduction

When implementing my openSource based RAP Generator I had the requirement that on the object page on item level fields should be read-only based on data that I had entered in the object page on header level.

So on the item level I had a mixture of fields that are read only by default and some that are read only based on the instance features.

So in my behavior definition I had something like this:

field ( features : instance ) MandFieldInstfeat;
field ( mandatory ) MandFieldBdef;

Though on the UI level we find that the fields are marked accordingly as read-only or mandatory based on the values being entered on header level

Header object page Item object page

“1” is entered on header level


–> the field MandFieldInstfeat is read-only.
“2” is entered on header level

–> the field MandFieldInstfeat is mandatory.

There is no out of the box support (so far) in RAP to validate the input of the user.

Code to control instance features

The method get_instance_features( ) is used by the RAP framework to determine which fields in the object page have to be set read-only or mandatory for each instance.

  METHOD get_instance_features.

    " get the root node(s). In a Fiori Elements UI this
    " will be just one entry. But when being called via EML or
    " as an API several instances of HeaderMand can be requested

    READ ENTITIES OF ZI_HEADERMand IN LOCAL MODE
    ENTITY iTEMmAND BY  \_HeaderMand
        ALL FIELDS
        WITH CORRESPONDING #( keys )
        RESULT DATA(HeaderMands).

    " Read all associated child nodes and set the field MandFieldInstfeat
    " to either read-only or mandatory based on the value of the field Mynumber
    " in the root node

    LOOP AT HeaderMands INTO DATA(HeaderMand).
      READ ENTITIES OF ZI_HEADERMand IN LOCAL MODE
        ENTITY HeaderMand BY \_ItemMand
          ALL FIELDS
        WITH VALUE #( ( %tky = HeaderMand-%tky ) )
        RESULT DATA(ItemMands).

      result = VALUE #( FOR ItemMand IN ItemMands
                            ( %tky                     = ItemMand-%tky
                              %field-MandFieldInstfeat = COND #( WHEN HeaderMand-Mynumber = 2
                                                                 THEN if_abap_behv=>fc-f-mandatory
                                                                 ELSE if_abap_behv=>fc-f-read_only )
                          ) ).
    ENDLOOP.
  ENDMETHOD.

 

Code to validate mandatory fields

The tricky thing is that we have to write code that finds out which fields

  1. have been defined as mandatory in the behavior defintion and
  2. have been defined as mandatory based on the features of our instance

and check whether data that has been entered by the user or the caller of the API for these fields.

While static settings as they are defined in the behavior definition are quite easy to spot it becomes more complicated if the status of a field can change dynamically, for example based on the content of another field of our business object.

GET PERMISSIONS

Whether a field of an entity is mandatory or not have to be retrieved using the statement GET PERMISSIONS.

This is (as I have to admit) not obvious because one would only expect to retrieve authorizations rather than the information whether a field is read-only, or mandatory.

The GET PERMISSIONS statement needs as an input a structure that must be typed with TYPE STRUCTURE FOR PERMISSIONS REQUEST. This structure (here called permission_request) contains a dynamic structure %field that contains the field names of our request. Using RTTI we can retrieve the field names dynamically in the internal table components_permission_request.

The result of the permission request contains instance specific information for each instance which is based on the (features : instance) statement such as:

field ( features : instance ) MandFieldInstfeat;

is stored in an internal table.

The static information which is based on statements such as

field ( mandatory ) MandFieldBdef;

is stored in a structure.

 

 METHOD mandatory_fields_check.

    DATA permission_request TYPE STRUCTURE FOR PERMISSIONS REQUEST ZI_ITEMMand.
    DATA reported_zi_itemmand_li LIKE LINE OF reported-itemmand.

    DATA(description_permission_request) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_data_ref( REF #( permission_request-%field ) ) ).
    DATA(components_permission_request) = description_permission_request->get_components(  ).

    LOOP AT components_permission_request INTO DATA(component_permission_request).
      permission_request-%field-(component_permission_request-name) = if_abap_behv=>mk-on.
    ENDLOOP.

    " Get current field values
    READ ENTITIES OF ZI_HeaderMand IN LOCAL MODE
    ENTITY ItemMand
      ALL FIELDS
      WITH CORRESPONDING #( keys )
      RESULT DATA(entities).

    LOOP AT entities INTO DATA(entity).

      GET PERMISSIONS ONLY INSTANCE FEATURES ENTITY ZI_ItemMand
                FROM VALUE #( ( itemuuid = entity-ItemUUID ) )
                REQUEST permission_request
                RESULT DATA(permission_result)
                FAILED DATA(failed_permission_result)
                REPORTED DATA(reported_permission_result).

      LOOP AT components_permission_request INTO component_permission_request.

        "permission result for instances (field ( features : instance ) MandFieldInstfeat;) is stored in an internal table.
        "So we have to retrieve the information for the current entity
        "whereas the global information (field ( mandatory ) MandFieldBdef;) is stored in a structure

        IF ( permission_result-instances[ itemuuid = entity-ItemUUID ]-%field-(component_permission_request-name) = if_abap_behv=>fc-f-mandatory OR
             permission_result-global-%field-(component_permission_request-name) = if_abap_behv=>fc-f-mandatory ) AND
             entity-(component_permission_request-name) IS INITIAL.

          APPEND VALUE #( %tky = entity-%tky ) TO failed-itemmand.

          "since %element-(component_permission_request-name) = if_abap_behv=>mk-on could not be added using a VALUE statement
          "add the value via assigning value to the field of a structure

          CLEAR reported_zi_itemmand_li.
          reported_zi_itemmand_li-%tky = entity-%tky.
          reported_zi_itemmand_li-%element-(component_permission_request-name) = if_abap_behv=>mk-on.
          reported_zi_itemmand_li-%msg = new_message( id       = '/DMO/CM_RAP_GEN_MSG'
                                                           number   = 066
                                                           severity = if_abap_behv_message=>severity-error
                                                           v1       = |{ component_permission_request-name }|
                                                           v2       = | with semantic key: { entity-SemanticKey } | ).
          APPEND reported_zi_itemmand_li  TO reported-itemmand.

        ENDIF.
      ENDLOOP.

    ENDLOOP.

 

RESULT

If the value “2” has been entered on header level and if as a result we have two mandatory fields on item level you will get the following error message if both fields are iniial.

because in the debugger we will find

and we have retrieved the field names as follows

 

Assigned Tags

      2 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Ramjee Korada
      Ramjee Korada

      Great Job Andre!

      this is another milestone to explore RAP.

      Author's profile photo Robson Soares
      Robson Soares

      Please, could you tell me if there is any way to replace the standard message with a custom message for the mandatory fields?

      The message displays the technical name of the field, which is not good for the end user.

      Thanks!