Friday 21 June 2024

How to replicate your Fiori Inbox in Microsoft Teams

Using Looply you can replicate your Fiori Inbox in Teams, and it’s fast and easy.

Putting outstanding workitems in the face of those users who are not logged into SAP Work Zone / Fiori LaunchPad / SAPGUI can accelerate processes and lead to a host of other benefits.

We can build the solution in 4 steps.

[1] Consider the trigger points.


You have options. You might run a background job periodically to update Teams. This is easy to set up, but resource-intensive if you decide to run it every few minutes instead of, say, twice per day.

You could trigger the Teams card from SAP workflow, so that you can be sure that the card is updated when a workitem is created and/or completed. This would mean adding tasks to each workflow, so it’s considerably more work, but much less resource intensive, as there is no background job to run.

You could trigger the Teams card from SAP events – in exactly the same way that workflows are triggered, so that each time a critical workflow is triggered then the Teams card is created or updated.  This is less work to set up, but potentially more complex because you might be triggering the Teams card before the workflow has determined the user. (So you might want to build in a 60-second wait.)

Of course, you could choose more than one approach, as some processes are more critical than others.  For example, you might trigger a card update at the end of specific workflow processes, and also schedule a job twice a day.

In this example we’ll write a simple ABAP program to schedule as a background job. (See step 4.)

[2] Build your Looply Workflow.


Pure notification workflows tend to be very simple, as basically Looply is being passed some data, which it is mapping to Teams card fields, and push the card to the identified user.

In this example we’re going to add a button to enable the user to remove the card, and also the capability to either CREATE a new card where one doesn’t exist, or UPDATE an existing card.

This way there will never be more than one notification card for a user.

How to replicate your Fiori Inbox in Microsoft Teams

Let’s break this down.

  1. When the workflow is triggered (and no existing card is in place) then the first step is to trigger a new card.
  2. The card could be closed manually (‘MS Teams Response’) or it could be subsequently updated from another SAP call (‘External Response')
  3. For the ‘External Response’ we need to capture the new data and push it into the data schema (‘payload’) that the card is mapped to. We do this mapping in the ‘Override Payload’ step.
  4. Then we Update the card, using the new data.
  5. Again, the user could close the card manually, or more data might be received from SAP. If more data is supplied then we update the schema from the data returned, and loop back to where we update the card.

[3] Build your Adaptive Card in Looply


The card is really simple as there are very few variables. It is basically a list of actions with URLS / deep links to point back to Fiori Inbox items.

How to replicate your Fiori Inbox in Microsoft Teams

This card comprises 5 containers, which are used to group and style fields:

  1. Heading
  2. User name
  3. Item count
  4. Item table
  5. Action buttons

The item table has just one field, for description, which is bound to “[${wi_text}](${wi_full})” such that the workitem text (wi_text) is displayed with the URL to the Workitem for action (wi_full).

[4] Build the SAP trigger


There are two parts to this. Firstly the Process Determination table should be configured, adding the Looply workflow ID.

How to replicate your Fiori Inbox in Microsoft Teams

Secondly, the trigger program:

How to replicate your Fiori Inbox in Microsoft Teams

Here we have created a simple selection screen just with the user name as the selection option.

The program logic is:

For each selected user:

  • Get details using BAPI_USER_GET_DETAILS
  • Get inbox items using SAP_WAPI_CREATE_WORKLIST
  • For each item, build the URL to the workitem in Fiori Inbox
  • Prepare data fields for the card in an internal table
  • Read the Looply Process Determination table
  • Trigger Looply workflow using /LOOPLY/CORE=>TRIGGER_WF for Outbound Scenario. This tries to start a new workflow for the user, but fails if there is already a workflow running.
  • If that fails trigger Looply workflow using /LOOPLY/CORE=>TRIGGER_WF for Resume Scenario.

This will continue the Looply workflow from the current step.

*&---------------------------------------------------------------------*
*& Report Z_LOOPLY_INBOX_DEMO
*&---------------------------------------------------------------------*
*& Demo code only
*&---------------------------------------------------------------------*
REPORT z_looply_inbox_demo.

TYPES: BEGIN OF ty_card_items,
         workitem TYPE string,
         wi_full  TYPE string,
         wi_text  TYPE string,
       END OF ty_card_items.

TYPES: tty_card_items TYPE TABLE OF ty_card_items WITH NON-UNIQUE DEFAULT KEY.

TYPES: BEGIN OF ty_card_data,
         user  TYPE string,
         name  TYPE string,
         email TYPE string,
         count TYPE string,
         items TYPE tty_card_items,
       END OF ty_card_data.

DATA: ls_card_items     TYPE ty_card_items,
      lt_card_items     TYPE TABLE OF ty_card_items,
      ls_card_data      TYPE ty_card_data,
      lv_items          TYPE i,
      lt_usr01          TYPE TABLE OF usr01 WITH HEADER LINE,
      ls_usr01          TYPE usr01,
      lv_subrc          TYPE sysubrc,
      lt_worklist       TYPE TABLE OF swr_wihdr,
      ls_worklist       TYPE swr_wihdr,
      lv_data           TYPE string,
      ls_looply_trigger TYPE /looply/activity,
      lv_process_id     TYPE /looply/process_id,
      lv_recipient      TYPE ad_smtpadr,
      ls_history        TYPE /looply/history,
      ls_address        type BAPIADDR3,
      lt_return         type bapiret2_t.

SELECT-OPTIONS: s_uname FOR lt_usr01-bname.

START-OF-SELECTION.

  SELECT * FROM usr01 INTO TABLE lt_usr01 WHERE bname IN s_uname.

  LOOP AT lt_usr01 INTO ls_usr01.

CALL FUNCTION 'BAPI_USER_GET_DETAIL'
  EXPORTING
    username                = ls_usr01-bname
  IMPORTING
    ADDRESS                 = ls_address
  tables
    return                  = lt_return.

    CLEAR: ls_card_items, lt_card_items[], ls_card_data.

    CALL FUNCTION 'SAP_WAPI_CREATE_WORKLIST'
      EXPORTING
        user        = ls_usr01-bname
        language    = sy-langu
      IMPORTING
        return_code = lv_subrc
      TABLES
        worklist    = lt_worklist.

    CHECK lv_subrc EQ 0.
    lv_items = lines( lt_worklist ).
    CHECK lv_items GT 0.

    READ TABLE lt_worklist INTO ls_worklist INDEX 1.

* Prepare card data - header level

    ls_card_data-user = ls_usr01-bname.
    ls_card_data-name = ls_address-fullname.
    ls_card_data-email = ls_address-e_mail.
    ls_card_data-count = lv_items.

* Item level

    LOOP AT lt_worklist INTO ls_worklist.
      ls_card_items-workitem = ls_worklist-wi_id.
      shift ls_card_items-workitem LEFT DELETING LEADING '0'.

      concatenate: '<link to inbox app>'
      ls_worklist-wi_id
      '/TaskCollection(SAP__Origin='
      `'LOCAL_TGW',InstanceID=` `'`
      ls_worklist-wi_id `'`
      ')'
      into ls_card_items-wi_full.

      ls_card_items-wi_text  = ls_worklist-wi_text.
      APPEND ls_card_items TO lt_card_items.
    ENDLOOP.

    ls_card_data-items = lt_card_items.

* Call Looply.

    lv_data = /ui2/cl_json=>serialize( data = ls_card_data pretty_name = /ui2/cl_json=>pretty_mode-low_case ).

    SELECT SINGLE * FROM /looply/activity INTO ls_looply_trigger
      WHERE scenario = 'OO'
      AND   scenario_id = 'WF_INBOX'
      AND   scenario_version = '00'
      AND   step = '1'.

    lv_recipient = ls_card_data-email.
    lv_process_id = ls_card_data-user.

      CALL METHOD /looply/core=>trigger_wf
        EXPORTING
          im_recipient         = lv_recipient
          im_looply_wf         = ls_looply_trigger-looply_wf
          im_looply_wf_version = ls_looply_trigger-looply_wf_version
          im_scenario          = ls_looply_trigger-scenario
          im_scenario_id       = ls_looply_trigger-scenario_id
          im_scenario_version  = ls_looply_trigger-scenario_version
          im_step              = ls_looply_trigger-step
          im_process_id        = lv_process_id
          im_data              = lv_data
        IMPORTING
          ex_subrc             = lv_subrc.

   IF lv_subrc EQ 0.
        WRITE: / 'Trigger: ',  lv_process_id.
   else.
      CALL METHOD /looply/core=>resume_wf
        EXPORTING
          im_process_id       = lv_process_id
          im_data             = lv_data
          im_scenario         = 'OO'
          im_scenario_id      = 'WF_INBOX'
          im_scenario_version = '00'
          im_step             = '1'
        IMPORTING
          ex_subrc            = lv_subrc.
      WRITE: / 'Resume: ',  lv_process_id.
    ENDIF.
  ENDLOOP.

No comments:

Post a Comment