Skip to Content
Technical Articles

Build a whole end-to-end process with SAP Fiori Launchpad Notifications

Hi all,

the SAP Fiori Launchpad becomes strategically more and more important to all customers. It also unveils great potential thinking of the customizing possibilities, the overarching access to relevant Fiori apps, transactions and also the seamless integration with other systems.

This includes the possibility to reimagine the alerting/notification idea and show all kinds of notifications in an independent service, the Notification Hub, accessible on the right top corner of the Launchpad (all the time, even if the user is in an app!).

Introduction

In this blog post, I want to illustrate a way of using the SAP Fiori Launchpad notifications, including the very poor documented capability of defining custom logic for quick actions (buttons within the notification) which can be added to an upcoming notification. Thereby we define the whole end-to-end process of triggering the notification, defining its content (with (navigation) parameters) and the action selected.

 

Prerequisites

Before we start setting up and implementing the notifications, we need to enable our SAP S/4HANA system to handle them. This pdf file (or this overview page) from the SAP Community Wiki describes the required steps in a very detailed manner.

At least for the SAP S/4 HANA release 1809, the demokit service (mentioned in one of the steps in the previously mentioned guide) is not available for activation. As an alternative to this, you can follow this SAP blog post to make the SAP Notification Hub active for your SAP Fiori Launchpad using a SAP-delivered catalog.

Last but not least, you need to maintain a new Notification Provider (just as you maintained the one in the step-by-step guide with the name ZIWNGW_DEMO1) with a new class, implementing the interface /IWNGW/IF_NOTIF_PROVIDER.

 

Getting Started: Our Use-Case

The use-case we want to realize in this blog post is derived from a customer requirement I recently coped with. A narrowed-down version of it is a great starting point to show, how SAP Fiori Launchpad notifications can enhance productivity greatly!

To be able to calculate compensation for an energy plant, it needs to be supported by the federal government. This “support” is basically only a factor in a database table, belonging to the unique identifier for the plant itself. An owner of each plant has to apply regularly for staying in the country’s support program. Therefore, we internally save not only the relevant factor but also the drop-off date, when the plant is no longer in the support program.

All customers currently have to manually check the relevant data for upcoming drop-offs, before they calculate the monthly compensation since the calculation report will fail if there is a plant without a support program (even though that sounds silly, that’s the law!). If this happens, the workers need to re-run the compensation report again, excluding the outdated plant.

To prevent this, we want a program checking on a monthly basis whether there is a drop-off candidate in the next month. Displaying this notification, the customer can either send a reminder E-Mail to the plant owner or just discard the message.

He/She can also navigate into the plant to further investigate it (e.g. extending the support date manually).

 

Creating the required methods (standard implementation) given by the interface

This is not the main part of this blog post, so I stay with standard implementation and without any further comments (we will mainly focus on the HANDLE_ACTION method and the report, triggering the notification). If you are interested in an explanation, I can recommend this blog post series.

GET_NOTIFICATION_PARAMETERS

  METHOD /iwngw/if_notif_provider~get_notification_parameters.
    DATA(lv_lang) = sy-langu.
    SET LANGUAGE lv_lang.
  ENDMETHOD.

 

GET_NOTIFICATION_TYPE

  METHOD /iwngw/if_notif_provider~get_notification_type.
    DATA ls_naction LIKE LINE OF et_notification_action.

    IF iv_type_key = 'EXPIRING_ANL'.
      " Return Notification Type and Version
      es_notification_type-version      = iv_type_version.
      es_notification_type-type_key     = iv_type_key.
      es_notification_type-is_groupable = abap_true.

      ls_naction-action_key = 'SendMailActionKey'.
      ls_naction-nature = /iwngw/if_notif_provider=>gcs_action_natures-positive.
      APPEND ls_naction TO et_notification_action.

      ls_naction-action_key = 'DismissActionKey'.
      ls_naction-nature = /iwngw/if_notif_provider=>gcs_action_natures-negative.
      APPEND ls_naction TO et_notification_action.
    ENDIF.
  ENDMETHOD.

 

GET_NOTIFICATION_TYPE_TEXT

  METHOD /iwngw/if_notif_provider~get_notification_type_text.
    DATA ls_naction_t LIKE LINE OF et_action_text.
    DATA lv_tmptext TYPE string.
    DATA lv_tmpattr TYPE string.
    DATA lv_st_text TYPE string.
    DATA lv_period TYPE string.
    CLEAR:es_type_text,et_action_text.

    DATA(lv_lang) = sy-langu.
    SET LANGUAGE iv_language.

    IF iv_type_key = 'EXPIRING_ANL'.
      es_type_text-template_public    = 'A plant will drop-off support soon'.
      lv_tmptext = 'The energy plant &1 will drop-off support on &2'.
      REPLACE '&1' WITH '{plant_id}' INTO lv_tmptext.
      REPLACE '&2' WITH '{expires_at}' INTO lv_tmptext.
      es_type_text-template_sensitive = lv_tmptext.
      es_type_text-description = 'End of support of a plant'.

      lv_tmptext = 'Soon &1 plant(s) drop-off support'.
      CONCATENATE '{' /iwngw/if_notif_provider=>gcs_parameter_reserved_names-group_count '}' INTO lv_tmpattr.
      REPLACE '&1' WITH lv_tmpattr INTO lv_tmptext.
      es_type_text-template_grouped = lv_tmptext.

      ls_naction_t-action_key = 'SendMailActionKey'.
      ls_naction_t-display_text = 'Send reminder E-Mail'.
      ls_naction_t-display_text_grouped = 'Send mass E-Mail'.
      APPEND ls_naction_t TO et_action_text.

      ls_naction_t-action_key = 'DismissActionKey'.
      ls_naction_t-display_text = 'Dismiss'.
      ls_naction_t-display_text_grouped = 'Dismiss all'.
      APPEND ls_naction_t TO et_action_text.
    ENDIF.

    SET LANGUAGE lv_lang.

  ENDMETHOD.

 

HANDLE_BULK_ACTION

  METHOD /iwngw/if_notif_provider~handle_bulk_action.
    RETURN. " No implementation for our use-case needed
  ENDMETHOD.

 

Enhancing the action with custom logic

After the setup of the required texts and headings, we want to focus on the mentioned actions: Send reminder E-Mail and Dismiss.

  METHOD /iwngw/if_notif_provider~handle_action.
    TYPES: BEGIN OF ty_lines,
             line TYPE char255,
           END OF ty_lines.
    DATA: lt_email_body    TYPE STANDARD TABLE OF ty_lines,
          lt_notif_id      TYPE /iwngw/if_notif_provider=>ty_t_notification_id,
          lv_email_subject TYPE string VALUE 'A plant will drop-off support soon',
          lv_email_body    TYPE string VALUE 'the energy plant &1 will drop-off support on &2.'.

    IF iv_notification_id IS INITIAL OR iv_action_key IS INITIAL.
      es_result-success = abap_false.
      es_result-action_msg_txt = 'Error: Key or ID initial'.
    ELSE.
      CASE iv_action_key.
        WHEN 'SendMailActionKey'. "Send E-Mail

          " Prepare and send E-Mail to the respective E-Mail address
          DATA(lt_notifications) = get_notifications_by_notifiid( iv_notifi_id = iv_notification_id ).

          LOOP AT lt_notifications INTO DATA(ls_notification).
            IF ls_notification-name = 'plant_id'.
              DATA(lv_plant_id) = ls_notification-value.
            ELSEIF ls_notification-name = 'expires_at'.
              DATA(lv_expires_at) = ls_notification-value.
            ENDIF.
          ENDLOOP.

          DATA(lv_plantowner) = get_plantowner_by_plantid(lv_plant_id).

          REPLACE '&1' WITH lv_plant_id INTO lv_email_body.
          REPLACE '&2' WITH lv_expires_at INTO lv_email_body.

          APPEND VALUE #( line = 'Dear Sir or Madam,' ) TO lt_email_body.
          APPEND VALUE #( line = lv_email_body ) TO lt_email_body.
          APPEND VALUE #( line = '' ) TO lt_email_body.
          APPEND VALUE #( line = 'Yours sincerely,' ) TO lt_email_body.
          APPEND VALUE #( line = 'The Plant-Compensation Team' ) TO lt_email_body.

          CALL FUNCTION 'EFG_GEN_SEND_EMAIL' " uses your logged-in E-Mail account
            EXPORTING
              i_title                = lv_email_subject
              i_sender               = sy-uname
              i_recipient            = lv_plantowner " a table full of recipients is also possible
              i_flg_commit           = 'X'
              i_flg_send_immediately = 'X'
            TABLES
              i_tab_lines            = lt_email_body
            EXCEPTIONS
              not_qualified          = 1
              failed                 = 2
              OTHERS                 = 3.

          IF sy-subrc = 0.
            es_result-action_msg_txt = |{ 'E-Mail successfully sent to' }{ lv_plantowner }|.
          ELSE.
            es_result-action_msg_txt = 'Error while sending the E-Mail'.
          ENDIF.

        WHEN 'DismissActionKey'. "Ignore notification
          es_result-action_msg_txt = 'Plant drop-off dismissed'.
        WHEN OTHERS.
      ENDCASE.
      es_result-success = abap_true.
      es_result-delete_on_return = abap_true.


" Delete notification for cleanup
      lt_notif_id = VALUE #( ( id = iv_notification_id type_key = iv_type_key type_version = iv_type_version ) ).

      TRY.
          /iwngw/cl_notification_api=>delete_notifications(
            EXPORTING
              iv_provider_id     = 'ZPW_LOC_NOTIFY'
              it_notification_id = lt_notif_id
          ).
        CATCH /iwngw/cx_notification_api INTO DATA(lrx_api).
           " Add event handling
      ENDTRY.
    ENDIF.
  ENDMETHOD.

 

Before we examine the code, I want to mention a workaround for being able to use the parameters (like the provided plant_id and expiring_at) in the HANDLE_ACTION method. Apparently, the data, which are imported to the interface method HANDLE_ACTION do not include the original parameter. Due to the fact that we need to have information regarding the respective plant and its drop-off date, this is a problem. Until today, I did not find any other way besides manipulating the notification ID to contain all the relevant information we need in order to execute the process correctly. This is accomplished by chaining the parameters, separated by an underscore: _ . Because this ID is an importing parameter to the HANDLE_ACTION method and can be processed further. (This manipulation of the notification ID happens in the trigger of the notification, as described in the next chapter.)

If you know a cleaner way (or even a method, designed for this purpose?) I would love to hear about that!

Having a closer look at the code, the doing is rather straight-forward: We separate the actions based on the action keys defined in GET_NOTIFICATION_TYPE_TEXT and GET_NOTIFICATION_TYPE. If we press the first button (SendMailActionKey), the “workaround-added” notifications are extracted (out of the notification ID (I used the SPLIT .. AT .. method from ABAP)). Afterwards, we used one of the extracted values (the plant ID) to find the corresponding plant owner’s E-Mail address (stored in a database table). As the last step, we inform the user whether the E-Mail was sent successfully or not. If the user dismissed the action, we do nothing. In the very end, we delete the clicked notification in order to keep the SAP Fiori Launchpad notification bar clean.

As we finished the notification action-handling, we can trigger it now!

 

Example of triggering the notification

For our use-case, we plan a background job that is automatically executed on a regular basis (each month). Make sure to not use e.g. sy-uname in important fields as you might encounter an issue through the background execution (I used it in EFG_GEN_SEND_MAIL, but this field is not relevant for the success of the function).

The job should check whether a plant drops-off support within the next month. If so, we send one notification for each candidate. Additionally, we want to use a combination of a semantic object and action (more on this topic available in this blog post) to be able to navigate to a Fiori App (with pre-defined filters), examining the plant further on.

REPORT zpw_get_dropoff_plants.

DATA lt_notif TYPE /iwngw/if_notif_provider=>ty_t_notification.
DATA lt_recipient TYPE /iwngw/if_notif_provider=>ty_t_notification_recipient.
DATA lt_nav_params TYPE /iwngw/if_notif_provider=>ty_t_navigation_parameter.
DATA lt_semantic_params TYPE /iwngw/if_notif_provider=>ty_t_notification_param_bundle.
DATA lt_notif_parameter TYPE /iwngw/if_notif_provider=>ty_t_notification_parameter.
DATA(lv_launchpad_role) = 'ZEEGBILLDISP'. "Your role having the launchpad notification catalog assigned
DATA(lv_type_key) = 'EXPIRING_ANL'. "Define your type key to identify this kind of notification
DATA lv_provider_id TYPE /iwngw/if_notif_provider=>ty_s_provider-id VALUE 'ZPW_LOC_NOTIFY'. " Your created provider ID

" Scan the database for upcoming drop-offs
DATA(lt_plants) = zcl_monitor_utilities=>check_for_dropoff_plants( ).

IF lines( lt_plants ) > 0.

" Get all users which are assigned to the launchpad notification role --> All should be notified
SELECT uname FROM agr_users INTO TABLE @DATA(lt_user)
    WHERE agr_name = @lv_launchpad_role AND from_dat < @sy-datum AND to_dat > @sy-datum.

LOOP AT lt_user INTO DATA(lv_user).
  APPEND VALUE #( id = lv_user ) TO lt_recipient.
ENDLOOP.

" Go through each plant and send an individual notification
LOOP AT lt_plants INTO DATA(ls_plant).

" For semantic navigation (via semantic object and action), we add the columns of the CDS table in the Fiori App 
lt_nav_params = VALUE #( ( name = 'plantId'    value = ls_plant-id ) ).

  APPEND VALUE #( name = 'plant_id' value = ls_plant-id type = /iwngw/if_notif_provider=>gcs_parameter_types-type_string is_sensitive = abap_false )
      TO lt_notif_parameter.

  APPEND VALUE #( name = 'expires_at' value = ls_plant-expires_at type = /iwngw/if_notif_provider=>gcs_parameter_types-type_string is_sensitive = abap_false )
      TO lt_notif_parameter.

  lt_semantic_params = VALUE #( ( language = sy-langu parameters = lt_notif_parameter ) ).

  lt_notif = VALUE #( (
  id                       = zcl_notification_provider=>get_notifiid_by_params( iv_plant = ls_plant-id iv_expiredate = ls_plant-expires_at )
  type_key                 = lv_type_key
  type_version             = '1'
  priority                 = /iwngw/if_notif_provider=>gcs_priorities-medium
  actor_id                 = sy-uname
  actor_type               = ''
  actor_display_text       = sy-uname
  recipients               = lt_recipient
  parameters               = lt_semantic_params
  navigation_target_object = 'plantmgmnt'
  navigation_target_action = 'manage'
  navigation_parameters    = lt_nav_params
  ) ).

  TRY.
      /iwngw/cl_notification_api=>create_notifications(
        EXPORTING
          iv_provider_id = lv_provider_id
          it_notification = lt_notif ).
    CATCH /iwngw/cx_notification_api INTO DATA(lrx_api).
      " Add error handling
  ENDTRY.

  COMMIT WORK.

ENDLOOP.

ENDIF.

 

Besides the extraction of the data and enter the navigation parameter, for this blog post, the most important part is the method which is called for the notification ID, as we need to chain the navigation parameter in it in order to be able to respond dynamically in the HANDLE_ACTION method. I recommend using, as already mentioned, underscores for chaining and separating individual navigation parameters in the ID.

It is also important to define a type key. Thereby we are able to reuse the class for different notification use-cases, you can easily split the whole logic by checking for the type key in each method.

To navigate to an app that allows the user to examine the plant further on, we have to provide the semantic object as the target object and the relevant action as target_action. As we want to specifically see only the relevant plant, we provide filter criteria (lt_nav_params) to the opened app, containing which column to filter and the corresponding value. In our use-case we wan to see the plant, and provide the plant ID.

 

Putting it all together: End-to-end process

That’s it! Let’s plan the background job and make sure, we have some plants that drop-off support soon.

The result will look like this:

In addition to the action for the first notification, we received the following E-Mail:

 

Conclusion

Notifications are a great way of providing a hint towards “Call for action” and for informing the user about various events and situations, relevant for his daily business. The SAP Fiori Launchpad is thought of being a single-entry point for all SAP-related activities, a user needs to perform. Combining these two conditions, notifications within this central tool bring great potential towards an integrated ecosystem and also automation. In addition to that, having the notifications centrally in the SAP Fiori Launchpad contributes to less E-Mail flooding, which causes a multiple proved lack of productivity, according to several corporate knowledge management types of research (even though we used a use-case which sends E-Mail 🙂 ).

Lots of teams underestimate the power of providing powerful notifications and the ease of use which comes by providing them – I hope you don’t!

 

Thanks, Peter

/
1 Comment
You must be Logged on to comment or reply to a post.