Skip to Content
Technical Articles
Author's profile photo Ramjee Korada

ABAP Restful Application Programming : Custom actions => Merge Draft entities into Active and switch to display mode

Introduction:

     In this blog post, I will explain about “Custom actions” that can merge draft entities into Active and switch to display mode. An action in RAP is a non-standard modifying operation that is part of the business logic. The standard use case of an action is to change specific fields of a business object entity.

Problem statement:

     In General, MODIFY ENTITIES can be used to change the required fields in an entity. That means, Active instance remains as active and Draft instance remains as Draft. But some business requirements expect us to convert those draft instances into active instances.
This can be achieved using “EXECUTE Activate” on MODIFY ENTITIES. With this, Active instance is prepared but the real challenge comes now.
     How does the fiori app come to know about this change to show modified information of the entity?
​Then “side effects” strike my mind but later I realize that they can be triggered to reflect modified information ( different property / entity ) on change of a field/entity/action  . 
But, In our case, Own entity needs to be refreshed. Actual challenge here is that the “original key” is “IsActiveEntity=false” while the modified key is “IsActiveEntity=true“.
 

Solution:

      As the entity instance itself is changing ( key ) through the action , we can declare the result parameter as “entity” with an “entity type” but not $self. That helps the fiori app to identify that the result instance is different from the initial instance. 
$self keeps the same instance even after action execution. 
 
Let us take an example action “CompletePR” that sets status code to “CMPL” on the header entity.
 
Please note that Base/Interface view entities need to be declared in Base Behavior Definition.
 
action CompletePR result [1] entity ZRK_I_PUR_REQ_H;
​The result projection view entities need to be exposed again in projection behaviour definition. ​
use action CompletePR result entity ZRK_C_PUR_REQ_H;​
During method implementation, We have to consider that there can be mixed instances of the input entities. Hence take out draft instances and apply “EXECUTE Activate” on MODIFY ENTITIES.
At this point of time, All the instances should be having active instances. So we have to read new instances to return to the fiori app in “result” .
So let’s prepare new keys where “%is_draft = if_abap_behv=>mk-off ” to read entities to get new instances.
It’s time to populate the result parameter and please note that

%key , %tky  to be filled from source instance while %param-%key to be filled from new instance. So that fiori app understands to fire “get” Call and retrive the new instance. As the result entity is an active instance, Fiori app automatically switches to display mode.

Below is the sample implementation.

  METHOD CompletePR.

*Modify the entities with required fields.
    MODIFY ENTITIES OF zrk_i_pur_req_h IN LOCAL MODE
      ENTITY _PRHead
      UPDATE FIELDS ( StatCode )
          WITH VALUE #( FOR key IN keys ( %tky = key-%tky
                                          StatCode ='CMPL' ) ).

* Check if there are any draft instances?
    DATA(lt_draft_docs) = keys.
    DELETE lt_draft_docs WHERE %is_draft = if_abap_behv=>mk-off.

    IF lt_draft_docs IS NOT INITIAL.

* EXECUTE Active only on draft instances.

        MODIFY ENTITIES OF zrk_i_pur_req_h IN LOCAL MODE
          ENTITY _PRHead
            EXECUTE Activate FROM
            VALUE #( FOR key IN keys ( %key = key-%key ) )
          REPORTED DATA(activate_reported)
          FAILED DATA(activate_failed)
          MAPPED DATA(activate_mapped).

   ENDIF.

* Change Keys to read active Instance
    DATA(lt_keys) = keys.
    LOOP AT lt_keys ASSIGNING FIELD-SYMBOL(<ls_key>).
      <ls_key>-%is_draft = if_abap_behv=>mk-off.
    ENDLOOP.

* Read the active instance to send back to Fiori App.
    READ ENTITIES OF zrk_i_pur_req_h IN LOCAL MODE
        ENTITY _PRHead
        ALL FIELDS WITH CORRESPONDING #( lt_keys )
        RESULT DATA(lt_pur_req).

* Populate %key , %tky  to be filled from source instance while %param-%key to be filled from new instance.
    result = VALUE #( for <fs_old_key> in keys
                      for <fs_new_key> IN lt_keys WHERE ( ObjectId = <Fs_old_key>-ObjectId )
                                                    ( %key = <fs_old_key>-%key
                                                      %tky = <fs_old_key>-%tky
                                                      %param-%key = <fs_new_key>-%key ) ).

    mapped-_prhead = CORRESPONDING #( lt_pur_req ).

  ENDMETHOD.​

As we can see, “Get” call is fired with new key and fiori app is switched to display mode.

Please share and comment your views in the comments section.

You can find my previous blogs on RAP at here .

Assigned Tags

      11 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Aman Garg
      Aman Garg

      This is very useful. Thank you for sharing Ramjee!

      Thanks,

      Aman Garg

      Author's profile photo Ramjee Korada
      Ramjee Korada
      Blog Post Author

      Thanks Aman.

      Author's profile photo Miller Lam
      Miller Lam

      Dear Ramjee Korada,

       

      Thanks for your selfless sharing. Such a wonderful sharing.

      Appreciate it.

       

      🙂

      ZFiori Studio

      Author's profile photo Ramjee Korada
      Ramjee Korada
      Blog Post Author

      Glad that you could find it useful.

      Author's profile photo Lukog Ton
      Lukog Ton

      @Ramjee Korada

      Thanks for the article. Could you please help me understand the diff b/w %key and %tky because you have used one modify statement with %tky and other with %key.

       

      Thanks

       

      Author's profile photo Ramjee Korada
      Ramjee Korada
      Blog Post Author

      Lukog Ton

      Yes, It is really needed to understand it better.

      %key is a technical key of the business object where as %tky is a transactional key which has additional identifier whether it is draft instance or active instance.

      It is not possible to identify draft or active instance with same key.

      Let us take an example.

      CDS BO has a key "ObjectId" = '1000001'.

      • %key => ObjectId = '1000001'
      • %tky => ObjectId , %Is_Draft
        • Draft instance -> ObjectId = '1000001' , %Is_Draft = 01
        • Active instance -> ObjectId = '1000001' , %Is_Draft = 00

      See documentation.

      Best wishes,

      Ramjee Korada

      Author's profile photo Florian Halder
      Florian Halder

      Hi Ramjee,

      I saw that you made a blog post out of your response to my community question. This is great, so it probably helps many more people. 🙂

      Just in case you didn't get a notification from my last comment.

      https://answers.sap.com/questions/13619757/rap-activate-draft-and-trigger-save-on-custom-acti.html?childToView=13620495

      If I use the back button after the execution of the custom action, the application displays the Draft-Version again and does not navigate to the report list page. Do the navigation work correctly on your example? If yes, do you have any idea where i might have made a mistake?

      Many thanks for your help!

      Kind regards Florian

      Author's profile photo Ramjee Korada
      Ramjee Korada
      Blog Post Author

      Hi Florian,

      I checked but I have not found any solution for navigation yet and I think it is not related to custom action but framework problem.

      I have compared with standard operation "SAVE" and there is also similar problem.

      1. Open list report with draft entity ( if you dont have any draft records, then create one and reload list report page )
      2. Navigate to object page of the draft instance
      3. Then save the document with standard save button
      4. The page is switched to display mode
      5. Navigate to list report page and you get the similar error.

      Best wishes,

      Ramjee Korada

      Author's profile photo Florian Halder
      Florian Halder

      Hi Ramjee,

      I'm not sure what you mean with similar error.

      With the standard save operation the navigation looks good. After save action (entity was in draft mode when entering the object page) I can directly navigate back to the report list page.

      But as described above with this custom action, it looks like we have two object pages. After switch to active version/display mode, it navigates to old object page (draft version/edit mode) when I use the "back button". If I use "back button" on this object page again, it navigates to the report list page.

      Thanks for your help with that. Maybe you have any other idea. 🙂

      Kind regards Florian

       

      Author's profile photo Florian Halder
      Florian Halder

      Hi Ramjee,

      I just tested my application with OData V4 instead of V2, where we don't have the "back button" within the app. With the back navigation of the browser (tested with Edge), the navigation seems to work correctly. 🙂

      Maybe you can verify this.

      Florian

       

      Author's profile photo Alexandra Köthe
      Alexandra Köthe

      Hi Ramjee,

      thanks very much for your post!!

      Can I do the same for Child entities?

      I would like to merge my root entity together with all its child entities to active.

      But "Execute activate" doesn't work for the child entity.

      How can I do this?

       

      Best reagrs,

      Alexandra