Skip to Content
Author's profile photo Ronald Konijnenburg

Make your any approval workflow mobile-ready (without SUP) // Android example

When I was reading excellent post about RESTful services via ICF by Michael Hardenbol I was wondering how to make some useful tool using presented techinques.

I decided to make some handy service for workflows based on standard User Decision step type (which is backed by DECISION object type). This service can be consumed by mobile – I’ve tested it with Android example application.

Of course you must consider all  security issues – presented approach uses no middleware, it depends of many factors when you can decide to use this kind of service.

The Service

ICF service concept is simple – allow to download list of decision workitems (http GET request) and send user’s decision back (PUT request).

For testing purposes the current user is taken from request headers (sap-user etc.) and switched on as a simple http service.Of course in production environment enable some stronger security available in ICF’s logon procedures. You can also make some additional layer for checking user credentials.

/wp-content/uploads/2012/10/s1_145527.jpg

The service have one handler class, which supports GET and PUT. I present some basic version for just for showing idea – think about potential vulnerabilities  and implement suitable protection.

METHOD if_http_extension~handle_request.

   TYPES: BEGIN OF ty_wi_info,
           wi_id     TYPE swr_structworkitemid,
           wi_title  TYPE swr_wihdrwi_text,
           wi_text   TYPE STANDARD TABLE OF swr_txtlin WITH NON-UNIQUE DEFAULT KEY,
           wi_htext  TYPE STANDARD TABLE OF swr_txtlin WITH NON-UNIQUE DEFAULT KEY,
           decisions TYPE STANDARD TABLE OF swr_decialts WITH NON-UNIQUE DEFAULT KEY,
          END OF ty_wi_info.

   DATA: verb        TYPE string,
         path        TYPE string,
         rc          TYPE sysubrc,
         split_tab   TYPE TABLE OF string,
         param1      TYPE string,
         param2      TYPE string,
         res_xml     TYPE xstring,
         dec_key     TYPE swr_decikey,
         dec_wiid    TYPE swr_structworkitemid,
         wi_text     TYPE swr_wihdrwi_text,
         nodes       TYPE TABLE OF swr_decialts,
         items       TYPE TABLE OF swr_wihdr,
         t_wi_info   TYPE TABLE OF ty_wi_info,
         wi_info     TYPE ty_wi_info.

   FIELD-SYMBOLS: <item> LIKE LINE OF items.

   verb = server->request->get_header_field( name = ‘~request_method’ ).

   IF verb <> ‘GET’ AND verb <> ‘PUT’.
     CALL METHOD server->response->set_header_field(
       name = ‘Allow’ value = ‘GET, PUT’ ).
     CALL METHOD server->response->set_status( code = ‘405’ reason = ‘Method not allowed’ ).
     EXIT.
   ENDIF.

   path = server->request->get_header_field( name = ‘~path_info’ ).
   SHIFT path LEFT BY 1 PLACES.
   SPLIT path AT ‘/’ INTO TABLE split_tab.

   CASE verb.  “Get user decisions to make…
     WHEN ‘GET’.

       CALL FUNCTION ‘SAP_WAPI_WORKITEMS_TO_OBJECT’
        EXPORTING
          objtype                        = ‘DECISION’
          top_level_items                = ‘ ‘
*     SELECTION_STATUS_VARIANT       = 0001     “active items
        IMPORTING
          return_code                    = rc
         TABLES
*     TASK_FILTER                    =
           worklist                       = items
*     MESSAGE_LINES                  =
*     MESSAGE_STRUCT                 =.
.
       IF rc <> 0.
         CALL METHOD server->response->set_status( code = ‘500’ reason = ‘Error (RC <> 0’ ).
         RETURN.
       ENDIF.

       LOOP AT items ASSIGNING <item> WHERE wi_aagent = syuname.

         “get details
         CLEAR: rc, wi_info, wi_text, nodes.
         CALL FUNCTION ‘SAP_WAPI_DECISION_READ’
           EXPORTING
             workitem_id          = <item>wi_id
          IMPORTING
            return_code          = rc
            decision_title       = wi_text
           TABLES
             alternatives         = nodes
*           MESSAGE_LINES        =
*           MESSAGE_STRUCT       =
                   .
         IF rc <> 0.
           CALL METHOD server->response->set_status( code = ‘500’ reason = ‘Error (RC <> 0’ ).
           RETURN.
         ENDIF.

         wi_infowi_id = <item>wi_id.
         wi_infowi_title = wi_text.
         wi_infodecisions = nodes.

         CLEAR rc.
         CALL FUNCTION ‘SAP_WAPI_WORKITEM_DESCRIPTION’
           EXPORTING
             workitem_id     = <item>wi_id
           IMPORTING
             return_code     = rc
           TABLES
             text_lines      = wi_infowi_text
             html_text_lines = wi_infowi_htext.

         APPEND wi_info TO t_wi_info.

       ENDLOOP.

       CALL TRANSFORMATION zwitoxml
         SOURCE root = t_wi_info
         RESULT XML res_xml.

       CALL METHOD server->response->set_header_field(
         name = ‘Content-Type’
         value = ‘text/xml; charset=utf-8’ ).

       CALL METHOD server->response->set_data( data = res_xml ).
       CALL METHOD server->response->set_status( code = ‘200’ reason = ‘OK’ ).

       RETURN.

     WHEN ‘PUT’.

       READ TABLE split_tab INTO param1 INDEX 2.   “…/wi_id/NNNNNNNNN/decision/NNNN
       READ TABLE split_tab INTO param2 INDEX 4.

       dec_key = param2.
       dec_wiid = param1.

       CLEAR rc.
       CALL FUNCTION ‘SAP_WAPI_DECISION_COMPLETE’
         EXPORTING
           workitem_id  = dec_wiid
           decision_key = dec_key
         IMPORTING
           return_code  = rc.

       IF rc <> 0.
         CALL METHOD server->response->set_status( code = ‘500’ reason = ‘Error (RC <> 0’ ).
       ELSE.
         CALL METHOD server->response->set_status( code = ‘200’ reason = ‘OK’ ).
       ENDIF.

   ENDCASE.

ENDMETHOD.

GET action returns XML with running decision tasks for calling user. It’s using standard WAPI methods for getting list of tasks and all possible decisions.

XML is generated with Simple Transformation:

<?sap.transform simple?>

<tt:transform xmlns:tt=”http://www.sap.com/transformation-templates“>

  <tt:root name=”ROOT”/>

  <tt:template>

    <workitems>

      <tt:loop name=”line” ref=”.ROOT”>

        <workitem>

          <id>

            <tt:value ref=”$line.wi_id”/>

          </id>

          <title>

            <tt:value ref=”$line.wi_title”/>

          </title>

          <description>

            <tt:loop name=”html” ref=”$line.wi_text”>

                 <tt:value ref=”$html.TEXTLINE”/>

            </tt:loop>

          </description>

          <decisions>

            <tt:loop name=”dec_line” ref=”$line.decisions”>

              <decision>

              <key>

                <tt:value ref=”$dec_line.ALTKEY”/>

              </key>

              <text>

                <tt:value ref=”$dec_line.ALTTEXT”/>

              </text>

              </decision>

            </tt:loop>

          </decisions>

        </workitem>

      </tt:loop>

    </workitems>

  </tt:template>

</tt:transform>

Each task has unique id, some title and description.

Each decision has four digit key (0001, 0002) and decribing text.

PUT action handles user decision sended in url: …/wi_id/NNNNNNNNN/decision/NNNN – after wi_id we have task id and after decision – decision number.

After parsing incoming data standard WAPI module is used for finishing decision task.

I made some example Android application to test. It connects to service via AsyncTask and downloads xml with awaiting decision tasks.

Business Workplace:

/wp-content/uploads/2012/10/s2_145595.jpg

/wp-content/uploads/2012/10/s4_145610.jpg

application:

Screenshot_2012-10-11-14-08-08.pngScreenshot_2012-10-11-14-09-39.png

Selecting one item shows work item details and possible decisions:

Screenshot_2012-10-11-14-10-35.pngScreenshot_2012-10-11-14-10-46.png

tapping button connects to service via PUT request and completes workitem.

Screenshot_2012-10-11-14-10-57.pngScreenshot_2012-10-11-14-12-05.png

Item is completed and removed from Business Workplace.

/wp-content/uploads/2012/10/s3_145606.jpg

Assigned Tags

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

      Hi Jacek,

      Very intersting blog.without using any middlewaredeveloped mobile app.

      As per your blog how to connect backend to mobile if there any connection is created or any other Add-on added to backend business suite.Can you please explain it Architecture of this App?

      Thanks,

      Syam.

      Author's profile photo David Clavey
      David Clavey

      The problem with not using any mobility middle ware, are two fold:

      1. All the communication and synchronisation error checking and correction code needs to be hand crafted 🙁 and fully tested 😕 in the client device. (You get it for free in most mobility middle ware)

      2. The mobility loading is fully on the enterprise server 🙁 . With mobility middle ware it is usually possible to communicate only the changes in data 🙂 , rather than a full replication of the data every synchronisation. And common held data, for instance lookup reference data can be cached on the mobility middle ware 🙂

      In conclusion, when developing code, its usually possible to not use a mobility middle ware during the initial stages because the loading of one or two mobile devices is neither here nor there. However if you are producing a mobility solution with 100+ connected devices, thats when you need to analyse the data and work out a synchronisation strategy that lighten the load on the server and tunes synchronisation speed.