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: 
LeonardoGomez
Product and Topic Expert
Product and Topic Expert
1,497
In this blog post I’m going to show you how I created a mobile card app very easily with SAP Cloud Platform Mobile Services. I recommend using this functionality when you want to release to the user simple information on mobile devices with very little time. You can even make use of push or add actions to be triggered by the user, but I’m not going to show that here.

 

Use Case


In this example we are going to imagine the case where we need to build a card to show the information that is shown in a tile of an SAP Fiori Overview Page. This is the Account Receivable Overview Fiori App. Let’s say we want to develop a mobile card with the information that we see in the Days Sales Outstanding card, marked in red on the right-hand side.


We don’t need much justification, this shows a KPI (Days Sales Outstanding, or DSO for short) that represents the time is taking to the company to collect sales. This is very important information for our user, so he wants to have it easily available in his mobile phone.

This card has two parts, a header with the average DSO in days, and a chart below that shows the actual value of the KPI by month compared to the best possible value. We need to find what’s the oData service that returns that information. With the help of the debugging tools in Chrome I find the name of the service and the requests corresponding to the average value and the chart.


The entity is C_ARDAYSSALESOUTSTDGOVW. I’m going to keep it exactly as it’s being used in the card. For the average value it’s a GET to:
 /C_ARDAYSSALESOUTSTDGOVW(P_DisplayCurrency=%27EUR%27)/Results?sap-client=100&$skip=0&$top=1&$select=DaysSalesOutstanding%2cDaysSalesOutstanding_E

 

The request for the chart is a GET to:
/C_ARDAYSSALESOUTSTDGOVW(P_DisplayCurrency=%27EUR%27)/Results?sap-client=100&$skip=0&$top=12&$orderby=EvaluationTimeFrameInMonths%20desc&$select=EvaluationTimeFrameInMonths%2cCalendarMonthName%2cDaysSalesOutstanding%2cDaysSalesOutstanding_F%2cDaysSalesOutstanding_E%2cBestPossibleDaysSalesOutstndng%2cBestPossibleDaysSalesOutstndng_F%2cBestPossibleDaysSalesOutstndng_E


SAP Cloud Platform Mobile Services


In my case I used my Neo trial account, but the service is almost identical in Cloud Foundry. I open the SAP Cloud Platform Mobile Services cockpit and, from there, SAP Mobile Cards option.


The first thing we need to do is to set up the destination to the backend system. We do that in the Features tab, inside the Connectivity feature.


I created a destination called UXM_CAL and we can test it’s working fine right from there. There is a button to send a ping and you can also explore the entities.


 

Create the SAP Mobile Card


Now we can create the mobile card and I choose a template from the ones available. I selected one called Mobile Service User Registration by Time just because I want to make use of the chart.


In the Info tab I give it a name and select the destination I created before.


Moving to the URLs tab I add my two urls that I’ve got from the DSO tile. I pasted them exactly as are being used in the Overview Page, with the same parameters. You can get the data and check that you see the correct result. You can notice there is a place to configure the parameters for the url but this way is working the same.


In the Editor tab I changed the code to adapt it to my use case. There is one HTML code for the front of the mobile card and another one for the back, plus a CSS for styling.


From the code of the front of the card I changed the title, description and the part that fills the chart with the data coming from the oData service. Now it looks like this:
<div id="mySimpleTemplate" data-type="text/x-handlebars-template">
<div class="charts-card-template">
<div class="header" style="padding-left: 16px">
<div style="height: 30px text-align: left;">
<span style="font-weight: bold; font-size: 24px; float: left; display: inline; padding-top: 16px">
Days Sales Outstanding
</span>
</div>
<div style="text-align: left;">
<span style=" font-size: 14px; clear: left; float: left; display: inline; padding-top: 4px">
by month, one year
</span>
</div>
</div>
<div id="user-registrations-chart" style="padding-top: 100px">
</div>
<script type="text/javascript">
var oData = sap.deck.renderers.context.currentlyRendering.renderer.model.oData;
//var statistics = oData.Statistics;
var statistics = oData.dataSets[0].data.d.results;
var data = [];

// Removed code from the template
/* for (var i = 0; i < statistics.length; i++) {
for (var j = 0; j < statistics[i].connectionList.length; j++) {
if (data.find(function(elem) {
return elem.key === statistics[i].connectionList[j].key;
})) {
data[j].value += statistics[i].connectionList[j].value;
} else {
data.push({
key: statistics[i].connectionList[j].key,
value: statistics[i].connectionList[j].value
});
}
} */
// End of removed code

// Inserted new code
for (var i = 0; i < statistics.length; i++) {
data.push({
key: statistics[i].CalendarMonthName,
value: statistics[i].DaysSalesOutstanding
});
}

var arrayOfZeroValues = data.filter(function(item) {
return item.value === 0;
});
// End of inserted code

if (arrayOfZeroValues.length === data.length) {
var chart = document.getElementById("user-registrations-chart");
chart.style.fontSize = "20px";
chart.style.textAlign = "center";
chart.innerHTML = "No Data";
} else {
var margin = {
top: 20,
right: 20,
bottom: 70,
left: 65
},
width = 330 - margin.left - margin.right,
height = 350 - margin.top - margin.bottom;
var values = data.map(function(d) {
return d.value;
});
var keys = data.map(function(d) {
return d.key;
});
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1).domain(keys);
var y = d3.scale.linear().range([height, 0]).domain([0, d3.max(values)]);
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
var svg = d3.select("#user-registrations-chart").append("svg").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g").attr("class", "x axis").attr("transform", "translate(0," + height + ")").call(xAxis).append("text").attr("dy", "+4em").attr("dx", "+8.5em").style("text-anchor", "middle").text("Months").style("font-size", "15px").style("font-weight", "bold");
svg.append("g").attr("class", "y axis").call(yAxis).append("text").attr("transform", "translate(-20,40) rotate(-90)").attr("y", 0).attr("dy", "-2.3em").attr("dx", "-2.5em").style("text-anchor", "end").text("Days").style("font-size", "15px").style("font-weight", "bold");
var bars = svg.selectAll("rect").data(data).enter().append("rect").attr("x", function(d) {
return x(d.key) + 5;
}).attr("y", function(d) {
return y(d.value);
}).attr("width", function(d) {
return x.rangeBand() - 10;
}).attr("height", function(d) {
return height - y(d.value) - 0.5;
}).style("fill", "#B1E0FD");
}

svg.selectAll('g.x.axis g text').each(function (d) {
var el = d3.select(this);
var splitText= d.split('/').reverse();
el.text('');

for (var i = 0; i < splitText.length; i++) {
var tspan = el.append('tspan').text(splitText[i]);
if (i > 0) {
tspan.attr('x', 0).attr('dy', '15');
}
}
});
</script>
</div>
</div>

Switching to the back of the card, I want to display what is shown in the header of the DSO tile. I replaced the HTML code with this one:
<div id="mySimpleTemplate" data-type="text/x-handlebars-template">
<div class="charts-card-template">
<div >
<span style="font-weight: bold; font-size: 24px; float: left; display: inline; padding:16px">
Days Sales Outstanding
</span>
</div>
<div style="clear:both;">
</div>
<hr>
<div>
<span style="float:left;margin-left:16px;margin-top:10px;padding-bottom:10px">
This card shows the total days sales outstanding by month for the last 12 months
</span>
</div>
<div style="clear:both;">
</div>
<hr>

<div >
<span id="dso-kpi" style="float:left;margin-left:16px;margin-top:10px;">
</span>
</div>
<div style="clear:both;">
</div>

<div >
<span id="dso-unit" style="float:left;margin-left:16px;margin-top:0px;">
Average DSO
</span>
</div>
<script type="text/javascript">
var oData = sap.deck.renderers.context.currentlyRendering.renderer.model.oData;
var dsokpi = document.getElementById("dso-kpi");
var dsokpivalue = document.createElement('span');
dsokpivalue.innerHTML = oData.dataSets[1].data.d.results[0].DaysSalesOutstanding;
dsokpi.appendChild(dsokpivalue);
</script>
</div>
</div>

I did a few changes in the CSS to make the labels of the chart readable and use another font for the back of the card.
html, body{
height: 100vh;
font-family: Helvetica;
}

.charts-card-template{
background: linear-gradient(to bottom right,#589CCE,#226596);
color: #FFF;
width: 100%;
height: 100vh;
display: block;
margin: auto;
}

.header {
background-color: solid #CEDDEC;
height: 40px;
padding-left: 16px;
border-bottom: 0,5px solid #CEDDEC;
}

hr{
margin: 0;
}

#card-info{
font-size: 15px;
padding: 10px 15px;
}

#no-data{
font-size: 20px;
text-align:center;
}

svg{
display: block;
margin: auto;
}

.axis text {
font-size:12px;
stroke: #CEDDEC;
fill: #fff;
shape-rendering: crispEdges;
stroke-width: 0;
}

.axis path, .axis line {
fill: none;
stroke: #CEDDEC;
shape-rendering: crispEdges;
}

.x.axis .tick text {
transform: rotate(-45deg) translate(-18px,5px);
}

/* new entries */
#dso-kpi{
font-family:72,72full,Arial,Helvetica,sans-serif;
font-weight: 400;
font-size: 40px;
background-color: none;
}

#dso-unit{
font-weight: 400;
font-size: 12px;
background-color: none;
}

At this point you can see a preview of how is going to look like. The last step is to publish the mobile card version to make it productive.



 

End Result


Users can use the QR code from their mobile devices to subscribe to the new mobile card. The end result looks like this:



 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Conclusion


I wish this may have helped you develop a new skill, solve and issue or that you bookmarked it for future use. I'm confident you will find it very easy. Some of you may fill more confortable with your preferred technology, but there is always room for a new tool in your toolbox, and besides, you can achieve this with just a little bit of HTML.

 
1 Comment