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.
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_struct–workitemid,
wi_title TYPE swr_wihdr–wi_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 sy–subrc,
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_struct–workitemid,
wi_text TYPE swr_wihdr–wi_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 = sy–uname.
“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_info–wi_id = <item>–wi_id.
wi_info–wi_title = wi_text.
wi_info–decisions = nodes.CLEAR rc.
CALL FUNCTION ‘SAP_WAPI_WORKITEM_DESCRIPTION’
EXPORTING
workitem_id = <item>–wi_id
IMPORTING
return_code = rc
TABLES
text_lines = wi_info–wi_text
html_text_lines = wi_info–wi_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:
application:
Selecting one item shows work item details and possible decisions:
tapping button connects to service via PUT request and completes workitem.
Item is completed and removed from Business Workplace.
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.
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.