Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
4,081

SAP Mobile Cards - Building Cards From Fiori Applications


 

 

SAP Mobile Cards


SAP Mobile Cards (formerly SAP Content To Go) provides a means for delivering mobile-consumable business content without the need to implement all of the associated business process in a mobile application.  The aim is to only provide the relevant business data in a form that can be accessed on a mobile device.  End users would continue to interact with the business processes through the normal desktop browser or application scenarios, only ‘publishing’ suitable content that will be consumed by the SAP Mobile Cards application.

(This section is copied from the “Abstract” of the “Architecture Concept for SAP Content To Go” document)



Requirements


This blog shows how to build SAP Mobile Cards based on existing Fiori applications implemented on Fiori Cloud Edition (FCE).  In such a scenario the following elements are required:

  • SAP Cloud Platform (SCP) instance

  • SCP Development & Operations Service (aka. SCP Mobile Services (SCPms)) enabled

  • Fiori application(s) to be rendered in the form of cards in SAP Mobile Cards

  • Relevant backend(s) for the Fiori application(s) that will be rendered as card(s) in SAP Mobile Cards


The approach in a nutshell


While creating a card in SAP Mobile Cards, the first step to achieve is to identify the structure of the data that will be used.  In the context of Fiori applications, the easiest way to proceed if you don’t know it yet is to call the Fiori app and trace the network activity from the browser.



Press the F12 key to access the debugger if you use Chrome.  Then, inspecting the different calls made while the application is loading should allow you to identify the services that are involved.  Once the relevant call identified, the URL involved will need to be cleaned up to remove the functions, search criteria and so on.  Criteria could also be added to reduce the amount of information retrieved from the backend but optimization is not the focus of this blog.

One this first step done, you will need to generate some sample data.  Those sample data will provide you some mock data to start building the interface of your card if you use the mapping feature from the editor.  Then you will finally build your card.

This blog will cover the scenario of leave request.  It will thus result in two different cards: one to check the status of requested leave and one to approve it.




Leave request


The purpose of this first card is to list all the leave requests issued so far and see what istheir status.

A mobile card consists in two things: a layout and a pointer to the data.  The first step will then be to clarify where the data should come from.

Identifying the oData source


As described in the approach section, the way to proceed is to navigate to the application in the browser and track the network communication for the relevant call(s).



Going through the list of entries on the left hand side we can assume that an entry with LeaveRequestCollection could be a good candidate.  Inspecting the result on the right hand side confirms that it is the call we were looking for.
https://********.***.***/sap/fiori/hcmmyleaverequestextension/sap/opu/odata/sap/HCM_LEAVE_REQ_CREATE...

This URL can be divided into tow parts.  The first part identifies the oData service
https://********.***.***/sap/fiori/hcmmyleaverequestextension/sap/opu/odata/sap/HCM_LEAVE_REQ_CREATE...

and the second identifies the set of filters, criteria that are used to specialized the query
?$filter=StartDate%20eq%20datetime%272018-02-01T00:00:00%27%20and%20EndDate%20eq%20datetime%272018-05-02T00:00:00%27%20and%20EmployeeID%20eq%20%2701063866%27&$select=StatusCode,StatusName,AbsenceTypeCode,AbsenceTypeName,StartDate,StartTime,EndDate,EndTime

Build a destination


A destination is required in order to get relevant data on the card.

Go to SAP Cloud Platform Development & Operations service ► Destinations.

Press the "New" button.

Leave the "Type" field set to "Mobile Destination"

Provide a "Destination Name"



Press Next

Provide a URL. It its preferable to limite the URL referenced here to the host of the server.

For the Rewrite Mode, select "No Rewriting".



Press Next



Press Next

Select "Basic Authentication" for the "SSO Mechanism" for the sake of simplicity.  This step should be though more seriously in productive situation...



Press Next

Provide your User Name and Password



Press Finish



This destination can now be used from any card that will be created.

Defining the card


Go to SAP Cloud Platform Development & Operations service ► Mobile Applications ► SAP Mobile Cards

Click on the create new card icon.

A couple of templates are available to avoid starting development from scratch.  Depending on the template source selected, an internal or external source can be chosen.



Provide a name and select a destination.

On the right hand side we should define the card type.  Depending on the type of card that is configured different input will be required or available for the configuration.


































Card Type Card Trigger Data Source for the Card
Welcome Card Any Device connecting to the Service automatically will get this Card. The card type can only have a single instance. the card can be a static card or contain data from any REST service returning json
Default Rest API will push this card to a user. The card type can have n instances of the card Data from any REST service returning json
Server Managed Card Any Device connecting to the Service can subscribe to this Card. The card type can only have a single instance. Data from any REST service returning json
Automatic Instance Creation Any Device connecting to the Service can subscribe to this Card. The query defined for this Card will return a result set. For each entity in the result set the Client will build a card. This card type can have n instances of a card. Data from any REST service returning json
Automatic Web Page Matching Rest API will push this card to a user. The card type can have n instances of the card Data from any REST service returning json


(source)


In this particular case, the "Server Managed Card" is the most suitable choice.

With this type of card, only a data endpoint URL should be provided.  The value to enter there will be the part that we ruled out when defining the destination:
?$filter=StartDate%20eq%20datetime%272018-02-01T00:00:00%27%20and%20EndDate%20eq%20datetime%272018-05-02T00:00:00%27%20and%20EmployeeID%20eq%20%2701063866%27&$select=StatusCode,StatusName,AbsenceTypeCode,AbsenceTypeName,StartDate,StartTime,EndDate,EndTime

You can also add optionally a short description for your card.



Save now your card before making anything else.

Setting some sample data


In the previous section we saw how to identify the oData source to build the card.  We will take the URL we could notice and run it in a browser.

As an SAP employee you will probably see that to get to the service your identity is picked up (using a certificate here).  What is important to mention is that the data that will be returned (at least using Chrome) will be in the XML format.  SAP Mobile Card requires a Json file for the sample data.

To get the right format, add the following string to the end of the URL:
&$format=json

NOTE: If this is the first parameter of the query part, a "?" will be required instead of the "&".

Running the modified URL should give you the expected result.  Download the file and import it into SAP Mobile Card sample data tab.

Building the card's user interface


In the previous step, we have seen how to build a shell for our card.  We have the connection to the data source, some sample data and some basic configuration.  The next task is now to implement the User Interface.

In the Editor tab of the interface, use the following code.
<div id="mySimpleTemplate" data-type="text/x-handlebars-template">
<div class="myTemplate">
<br style="clear:left;">
<div style="margin-top:20px;margin-left:10px;">
<span style="float:left;">Employee ID</span>
</div>
<br style="clear:left;">
<div style="margin-left:10px;">
<span style="font-weight:bold;float:left;">{{Address}}</span>
</div>
<br style="clear:left;">
<div style="margin-top:20px;margin-left:10px;">
<span style="float:left;">History</span>
</div>
{{#each Opportunities}}
<br style="clear:left;">
<div style="margin-left:10px;">
<span style="font-weight:bold;float:left;width:30%;font-size:14px">{{Description}}</span>
<span style="font-weight:bold;float:left;width:30%;font-size:14px">{{Probability}}</span>
<span style="font-weight:bold;float:left;width:10%;font-size:14px">{{ExpRevenue}}</span>
<span style="font-weight:bold;float:left;width:30%;font-size:14px">{{Currency}}</span>
</div>
{{/each}}
</div>
</div>

As you can see in the code above, some words/instructions exist between double curly braces, it is called "handlebars".  It represents the variables or the place holders for the data that should come from the sample data in the first place and later from the actual data from the data source.  For more information related to Handlebars, you can check this link.

The name given to the variables are not inline with the attributes that exist in the sample data file.  This is to highlight the independence of those two elements of the development...  The question is now: Where do we make the mapping?

The mapping between the variable used in the Editor and the info that can be retrieved from the data source are mapped in the Data Mapping tab...



The card we have implemented might display way too much information than expected.  To better control what needs to be displayed on the card and how far we should look back for leave requests we can use the following script which uses JScript.  Unfortunately JScript cannot be previewed in the Data Mapping tab which ruins a bit the added value provided by the integrate IDE.
<div id="mySimpleTemplate" class="myTemplate" data-type="text/x-handlebars-template">
<div class="header">
<h1>
Leave Request
</h1>
</div>
<div class="card-content">
<div>
<h2>Requests issued</h2>
</div>
<div id="requestList">
</div>
<script type="text/javascript">
var oData = sap.deck.renderers.context.currentlyRendering.renderer.model.oData;
var vacation = [];
var history = 6;
var d = new Date();
var o = new Date(d.getFullYear(), d.getMonth() - history, d.getDate());
var i;
i = 0;
for (let data of oData.d.results){
if (d >= o) {
vacation[i] = {
sd: Date(data.StartDate),
ed: Date(data.EndDate),
num: data.WorkingDaysDuration,
status: data.StatusName,
type: data.AbsenceTypeName
}
i++;
};
};

var txt = "";
for (let item of vacation){
txt = txt + "<br>" + item.toString();
}

document.getElementById("requestList").innerHTML = txt;
</script>
</div>
<img src="logo.png" alt="" style="width: 6rem; height: 6rem; position: absolute;
bottom: 5rem; right: 3rem;" >
</div>

Nothing else is required for this first scenario.

 

Possible enhancements and limitations


Ideally it would also be nice to see the current balance of available days.  Unfortunately this highlights some challenges for the future:

  • How to build a card combining data coming from different oData services?

  • How to build a card relying on a function that needs to be ran on an oData service?

  • How to call a function with parameter related to the identity of the user (User ID)?


A last enhancement point could be the versioning.  Until now, no versioning feature have been implemented which makes it important to download the cards to have the option to rollback if something had to go wrong.



 

Leave Request Approval


In the previous section we have seen how to create a card that lists all the leave requests that have been issued along with their status.  The other side of this process is having the line manager to approve or deny the requests that were generated by his team members.  The idea will thus be to create as many cards as there are leave requests on which a decision can be made to accept or deny the request.

Identifying the oData source


Tracing the network activity while loading the app will just as before give us the information we need to build the destination and the endpoint URL.  We need to identify the call that will provide the required details to build a card for each leave request.
https://********.***.***/sap/fiori/hcmapproveleaverequestextensio/sap/opu/odata/sap/HCM_LEAVE_REQ_AP...

If we rework the URL to fit it to our needs we will get the following:
https://********.***.***/sap/fiori/hcmapproveleaverequestextensio/sap/opu/odata/sap/HCM_LEAVE_REQ_AP...

While the "Requester Name", the "Leave Type Description", the "Start Date", the "End Date" and the "Absence Days" are obvious, the "Request Id" is required to implement the actions that should be made for each leave request.

Defining the card


The idea is to create as many cards as there is Leave Requests to approve.  The key elements to configure are thus:
























Card Type Automatic Instance Generation
Query /sap/fiori/hcmapproveleaverequestextensio/ sap/opu/odata/sap/HCM_LEAVE_REQ_APPROVE_SRV/ LeaveRequestSet?$select=RequestId,RequesterName, LeaveTypeDesc,StartDate,EndDate,AbsenceDays
Collection Handling Use Collection
Query URL Collection Root $.d.results
Query Entity Key Paths $.d.__metadata.uri


This configuration of the card will generate a card for each entry in the d.results node of the Json file returned when querying the destination with the specified query.  The structure of the card should rely on the metadata provided.

Building the card's user interface


The code bellow defines a simple way to present the key information that will be necessary to approve or reject the request.


<div id="mySimpleTemplate" class="myTemplate" data-type="text/x-handlebars-template">
<div class="header">
<h1>
Leave Request to approve
</h1>
</div>
<div class="card-content">
<h2>
{{RequesterName}} has requested {{AbsenceDays}} days of {{LeaveTypeDesc}}:
</h2>
<div class="content">
<p>
From {{StartDate}} to {{EndDate}}.
</p>
</div>
</div>
<img src="logo.png" alt="" style="width: 6rem; height: 6rem; position: absolute;
bottom: 5rem; right: 3rem;" >
</div>

In the Data Mapping tab we can link the variable from the design to the variable from the data source.



At this stage, we have defined a card with the relevant information but we still need the capability to react upon it.

Implementing actions


Identifying the oData service


What is required now is to identify the call that is made to approve or reject a leave request.  This is once again done tracing the network activity while using the Fiori application during those operations.

Approve


https://********.***.***/sap/fiori/myapprovalinbox/sap/opu/odata/sap/HCM_LEAVE_REQ_APPROVE_SRV/Apply...

The URL above can be divided into three parts: the host for the destination, the service and the query.  Inside the query (everything that follows the ?) we can see the "Decision" parameter and its value "PREPARE_APPROVE".  The "Comment" attribute is empty.

Reject


https://********.***.***/sap/fiori/myapprovalinbox/sap/opu/odata/sap/HCM_LEAVE_REQ_APPROVE_SRV/Apply...

This time the "Decision" parameter has the value "PREPARE_REJECT" and the parameter "Comment" has some text.

Defining the actions


In the Action tab and in the Behavior section, select DELETE for the "Behavior after Action".  This option will automatically delete the card from the client as soon as a decision is made.

Then you need to reference the XCSRF Token URL (Cross-Site Request Forgery).  This URL is there to establish a “presession” to obtain the CSRF token that will be required in the subsequent operations as a secret validation token against CSRF attacks. (link)

NOTE: “Cross-Site Request Forgery (CSRF) is a type of attack that occurs when a malicious web site, email, blog, instant message, or program causes a user’s web browser to perform an unwanted action on a trusted site for which the user is currently authenticated” (source).


Approve


In the Action tab, press the "+" button next to the "Actions" title.

Provide a Name and a Label to the action.

Type in the following URL.
/sap/fiori/myapprovalinbox/sap/opu/odata/sap/HCM_LEAVE_REQ_APPROVE_SRV/ApplyLeaveRequestDecision?SAP__Origin='undefined'&RequestId='${RequestId}'&Version=1&Comment=''&Decision='PREPARE_APPROVE'

As you can see we need to reference the RequestId.

 



Then Add one request header and one parameter to the action.
Request Header

This request header is required as a CSRF Specific defense on top of the secret token validation already mentioned.  This defense relies on the same-origin policy (source).  The idea is to guarantee that the source hasn’t been changed over time, that the request is not replayed or issued from another location, outside the session initiated originally.

Custom HTTP headers can be used to prevent CSRF because the browser prevents sites from sending custom HTTP headers to another site but allows sites to send custom HTTP headers to themselves using XMLHttpRequest.  (link)

NOTE: There are three mechanisms a site can use to defend itself against cross-site request forgery attacks: validating a secret token, validating the HTTP Referer header, and including additional headers with XMLHttpRequest.  (link)


Parameter

The parameter added here is not useful since it is not even referenced in the Action URL as it will be the case for the Reject.  It is just added by systematism...




Reject


In the Action tab, press the "+" button next to the "Action" title.

Provide a Name and a Label to the action.

Type in the following URL:
/sap/fiori/myapprovalinbox/sap/opu/odata/sap/HCM_LEAVE_REQ_APPROVE_SRV/ApplyLeaveRequestDecision?SAP__Origin='undefined'&RequestId='${RequestId}'&Version=1&Comment='${comments}'&Decision='PREPARE_REJECT'

We still have the reference to the RequestId bu also a reference to a variable "comments".  This comment is provided during the rejection of the request.  We will create this parameter in the next step.



Then Add one request header and one parameter to the action.
Request Header

As for the accept action add the same request header.


Parameter

This time the comments parameter is required since we made a reference to it in the action URL.





The configuration of the action for our second cards is done...

 

Related content



  • Product Jam page here

  • Product info page here

  • Tutorial on SAP Mobile Cards here

  • Blog post giving quick overview of the product here

  • Blog post descibing how to enable Purshase Order Approval in SAP Mobile Card here


Acknowledgment


Thanks a lot for the support provided by Sami Lechner.
4 Comments