Overview
SAP has built a great library of controls for generating colorful, animated, snappy reports for the business.
Here’s a simple example of an SAP UI5 application built with Eclipse and published to an SAP BSP application. The report displays the results of an ‘Average Turn Time’ report in a pie chart. It offers inputs to filter the data to only Temporary changes or all changes and also lets the user adjust the date range.
Dependencies
SAP ERP software component UISAPUI5 (SAP UI5)
Eclipse (I’m using Version: 4.2.1)
SAP HTML5 SDK (I’m using version 1.12.1)
- SAP core library (installed on a server)
- client software (installed in Eclipse – helpful post)
JSON HTTP Handler
First I needed a way to convert the results from an SAP function module to JSON format to be consumed by the SAP UI5 components. I found a project on SAP SCN that converts function module results to JSON. I set it up in SICF.
https://cw.sdn.sap.com/cw/groups/json-adapter-for-abap-function-modules
I had to make one small change to use a different class for the actual JSON conversion. In method SERIALIZE_ID of class ZCL_JSON_HANDLER, instead of calling the transformation, I’m using class CL_TREX_JSON_SERIALIZER.
1: * call transformation id options data_refs = 'embedded'
2: * initial_components = 'include'
3: * source (stab)
4: * result xml json_writer.
5: * o_string = cl_abap_codepage=>convert_from( json_writer->get_output( ) ).
6: CREATE OBJECT json_writer
7: EXPORTING
8: data = stab.
9: json_writer->serialize( ).
10: o_string = json_writer->get_data( ).
SAP UI5 Application
In Eclipse, I created the SAP UI5 application zem_ui5_gcs01. The application uses a very simple index.html. However, be sure to include the additional library: sap.viz.
1: <!DOCTYPE html>
2: <html><head>
3: <meta http-equiv='X-UA-Compatible' content='IE=edge' />
4: <title>Average Turn Time</title>
5:
6: <script id='sap-ui-bootstrap' type='text/javascript'
7: src='http://[host-to-sap-ui5-core]/sapui5/resources/sap-ui-core-all.js'
8: data-sap-ui-theme='sap_goldreflection'
9: data-sap-ui-libs='sap.ui.commons, sap.viz'></script> 1:
2:
3: <script>
4: sap.ui.localResources('avg_turn_time');
5: var view = sap.ui.view({
6: id: "main1",
7: viewName:"avg_turn_time.main",
8: type:sap.ui.core.mvc.ViewType.JS
9: });
10: view.placeAt('content');
</script>
10:
11: </head>
12: <body class='sapUiBody'>
13: <div id='content'></div>
14: </body>
15: </html>
It calls function module Z_E_RFC_GCS_AVG_TURN_TIME. This returns in the following format:
1: {
2: "et_actions": [
3: { "action":"00012", "display_order":"00002", "task":"Through Request",
4: "day_count":1, "record_count":99, "day_count_2":1, "record_count_2":99 },
5: { "action":"00046", "display_order":"00003", "task":"Through Analysis",
6: "day_count":1, "record_count":99, "day_count_2":0, "record_count_2":99 },
7: { "action":"00027", "display_order":"00004", "task":"Through Approval",
8: "day_count":2, "record_count":99, "day_count_2":1, "record_count_2":99 },
9: { "action":"00088", "display_order":"00005", "task":"Through Implementation",
10: "day_count":49, "record_count":91, "day_count_2":48, "record_count_2":91 },
11: { "action":"00000", "display_order":"00006", "task":"Through Archive",
12: "day_count":59, "record_count":91, "day_count_2":10, "record_count_2":91 }
13: ],
14: "e_record_count":100
15: }
The SAP UI5 javascript application contains just one view avg_turn_time/main.view.js, that contains the UI for the report.
1: sap.ui.jsview("avg_turn_time.main", {
2:
3: getControllerName : function() {
4: return "avg_turn_time.main";
5: },
6:
7: createContent : function(oController) {
8: var controls = [];
9:
10: var btn1 = new sap.ui.commons.Button({
11: id: this.createId('btn1'),
12: text: 'press me',
13: press: [ oController.button_press, oController ]
14: });
15:
16: var lblTemp = new sap.ui.commons.Label(this.createId('lblTemp'), {
17: text: "Temporary:"
18: });
19: var cbTemp = new sap.ui.commons.CheckBox(this.createId('cbTemp'), {
20: checked: true,
21: change: [ oController.checkbox_change, oController ]
22: });
23:
24: var lblDate = new sap.ui.commons.Label(this.createId('lblDate'), {
25: text: "Date:"
26: });
27: var date1 = new sap.ui.commons.DatePicker(this.createId('date1'), {
28: yyyymmdd: '20130101' ,
29: change: [ oController.date_change, oController ]
30: });
31: var date2 = new sap.ui.commons.DatePicker(this.createId('date2'), {
32: yyyymmdd: '20130105',
33: change: [ oController.date_change, oController ]
34: });
35:
36: var dataset2 = new sap.viz.ui5.data.FlattenedDataset({
37: dimensions : [
38: { axis : 1, name : 'Task', value : '{task}' },
39: ],
40: measures : [
41: { name : 'Average', value : '{day_count_2}' }
42: ],
43: data : { path : '/et_actions'}
44: });
45:
46: var pie1 = new sap.viz.ui5.Pie(this.createId('pie1'), {
47: dataset: dataset2
48: });
49:
50: controls.push(
51: lblTemp, cbTemp,
52: lblDate, date1, date2,
53: btn1,
54: pie1
55: );
56:
57:
58: return controls;
59: }
60:
61: });
In the controller, we create a ‘refresh’ routine to call the web service with selected input parameters.
1: sap.ui.controller("avg_turn_time.main", {
2:
3:
4: /**
5: * Called when a controller is instantiated and its View controls (if available) are already created.
6: * Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization.
7: */
8: onInit: function() {
9: },
10:
11: /**
12: * Similar to onAfterRendering, but this hook is invoked before the controller's View is re-rendered
13: * (NOT before the first rendering! onInit() is used for that one!).
14: */
15: onBeforeRendering: function() {
16: },
17:
18: /**
19: * Called when the View has been rendered (so its HTML is part of the document). Post-rendering manipulations of the HTML could be done here.
20: * This hook is the same one that SAPUI5 controls get after being rendered.
21: */
22: onAfterRendering: function() {
23: //var txt1 = this.byId('txt1');
24: //txt1.focus();
25: this.refresh(undefined);
26: },
27:
28: /**
29: * Called when the Controller is destroyed. Use this one to free resources and finalize activities.
30: */
31: // onExit: function() {
32: //
33: // }
34:
35: button_press: function(event) {
36: this.refresh(event);
37: },
38:
39: date_change: function(event) {
40: this.refresh(event);
41: },
42:
43: checkbox_change: function(event) {
44: this.refresh(event);
45: },
46:
47: refresh: function(event) {
48: sap.ui.getCore().setModel(undefined);
49:
50: var url = 'http://<sap-host>:<sap-port>/keg/rfc/Z_E_RFC_GCS_AVG_TURN_TIME/lc?I_TEMPLATE=00001';
51:
52: var cbTemp = this.byId('cbTemp');
53: if (cbTemp.getChecked() == true) {
54: url += '&I_TEMPORARY_FLAG=X';
55: }
56:
57: var date1 = this.byId('date1');
58: var date2 = this.byId('date2');
59: url += '&I_SEARCH_DATE=IBT' + date1.getYyyymmdd() + date2.getYyyymmdd();
60:
61: var oModel4 = new sap.ui.model.json.JSONModel(url);
62: sap.ui.getCore().setModel(oModel4);
63: }
64: });
Create the SAP BSP Application
I understand that Eclipse has the ability to connect to the SAP host and generate the BSP application automatically. Unfortunately, due to our system release, that feature is not available to my team.
However, SAP has provided a very nice compromise. Report /UI5/UI5_REPOSITORY_LOAD can be used to upload the Eclipse JavaScript application to SAP as a new BSP application. Some items of note:
- Our system has no default code page. Use Cp1252.
- Activate node /default_host/sap/bc/ui5_ui5 (and subnodes).
- During the upload, nodes are generated in two places. Make sure they are all activated prior to execution.
/default_host/sap/bc/ui5_ui5/sap/[z_bsp_name]
/default_host/sap/bc/bsp/sap/[z_bsp_name]
Now, you’ll have access to an application at the following location.
http://[sap-host]:[sap-port]/sap/bc/ui5_ui5/sap/zem_ui5_gcs01/index.html
The result is what you see above – a cool pie chart of average time through each stage of a process. With some more coding in events of the pie chart control, I’m pretty sure we could build some fancy drill-down capabilities… haven’t explored that yet.
Enjoy!