Introduction
In this blog I demonstrate how to create an SAPUI5 frontend for SAPs Solution Sales Configuration system.
SAP Solution Sales Configuration is a system that helps customers to configure and sell solutions made of complex product combinations. It is integrated into both CRM and ECC.
See here for more details - https://help.sap.com/saphelp_ssc10/helpdata/en/f4/ee271973864f48ad502c070b50ef8a/frameset.htm
http://www.youtube.com/watch?v=Y3ogJzOtkVA
The UI will be dynamically generated at runtime creating the master lists and detail controls based on the instances and characteristics of the configuration.
In this application I am using the “sap.m.*” library controls to develop the UI for a mobile solution. For the layout I am using a splitApp control so that I have the master page on the left hand side that will contain a list of instances in the configuration and a detail page on the right hand side which will contain the characteristics of the instances. And of course when run on a mobile device the UI will be split so that you will see either the master page or the detail page. See here for more details on the SplitApp control - https://sapui5.netweaver.ondemand.com/sdk/#docs/guide/SplitApp.html
High Level Description
Details
SSC_UI5_V2.view.js
In the view create the SplitApp control and add a dummy master and detail. The dummy pages are added so that the SplitApp control gets initialized properly. You may run into javascript errors on initialization if not added.
sap.ui.jsview("ssc_ui5_v2.SSC_UI5_V2", {
getControllerName : function() {
return "ssc_ui5_v2.SSC_UI5_V2";
},
createContent : function(oController) {
jQuery.sap.require("jquery.sap.resources");
var oSplit = new sap.m.SplitApp("splitApp");
//Add dummy master and detail pages so that the splitApp gets initialized
var oDummyMaster = new sap.m.Page("dummyMaster");
var oDummyDetail = new sap.m.Page("dummyDetail");
oSplit.addMasterPage(oDummyMaster);
oSplit.addDetailPage(oDummyDetail);
oSplit.setMode("ShowHideMode");
return oSplit;
}
});
web.xml
In the web.xml define the servlet mapping as follows:
<!-- ============================================================== -->
<!-- SSCServlet servlet -->
<!-- ============================================================== -->
<servlet>
<servlet-name>SSCServlet</servlet-name>
<servlet-class>com.ssc.ui.demo.SSCServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SSCServlet</servlet-name>
<url-pattern>/SSCServlet/*</url-pattern>
</servlet-mapping>
SSC_UI5_V2.controller.js (init function)
On initialization of a SAPUI5 application the onInit function is called. Here we send a request to the SSCServlet URL which was defined in the previous step in the web.xml file. If the call is successful then we should receive a Json response object with the details of what instances and characteristics we need to display in the UI. This Json object is passed to the createUIControls function which will create the controls for the UI.
onInit : function() {
$.ajax({
type : "POST",
url : "SSCServlet",
data : {
command: "initialize"
},
success : function(data) {
sap.ui.controller("ssc_ui5_v2.SSC_UI5_V2").createUIControls(data);
},
error : function(data) {
alert("Failed:" + data);
}
});
},
SSCServlet.java
As stated above the controller init function sends a request to the SSCServlet object and the doGet method is called. The doGet method specifies a json response content type and processes the request. For initialize it calls the getUIElements method of the SSCClient object, and writes the output to the response object.
public class SSCServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
JsonObjectBuilder objectBuilderOut = Json.createObjectBuilder();
String action = request.getParameter("action");
response.setContentType("application/json");
if (action.equals("initialize"))
{
SSCClient client = new SSCClient();
JsonObject resp = client.getUIElements();
response.getWriter().print(resp);
}
else if (action.equals("change"))
{
.
.
.
SSCClient.java
When the getUIElements method of the SSCClient is called it calls a number of methods in the SSCClient object to talk to the SSC engine and pull out the instance and characteristic data of the configuration. Here are some of the main features of this code:
IConfigSession cnfgSession = new ConfigSessionLocalImpl();
cnfgSession.createSession("true", sessionId, dataSourceName, false,
useOnlyLocalDatasource, languageISOCode);
String configId = cnfgSession.createConfig(rfcConfigId, productId,
productType, kbLogSys, kbName, kbVersion, kbProfile,
kbIdInt, kbDateStr, kbBuild, useTraceStr, context,
setRichConfigId, useLocalDeltaBean);
IInstanceData[] instData = cnfgSession.getInstances(configId, getMimeObjStr);
ICsticData[] allCstics = cnfgSession.getCstics(configId, instId);
.
.
if (csticDat.getCsticVisible())
{
labelID = csticDat.getCsticHeader().getCsticName() + "Lab"+ inst;
objectBuilderLabel.add("id", labelID);
objectBuilderLabel.add("text", csticDat.getCsticHeader().getCsticLname());
objectBuilderLabel.add("design", "sap.ui.commons.LabelDesign.Bold");
formEleID = csticDat.getCsticHeader().getCsticName() + "FE"+ inst;
objectBuilderFormElemProps.add("id", formEleID);
objectBuilderFormElem.add("properties", objectBuilderFormElemProps);
objectBuilderFormElem.add("Label", objectBuilderLabel.build());
objectBuilderFormElem.add("ResponsiveFlowLayoutData", objectBuilderRfld.build());
ICsticValueData[] csticVal = csticDat.getCsticValues();
String val = null;
if (csticVal != null && csticVal.length > 1 && !csticDat.getCsticDomainIsInterval())
{
if(csticDat.getCsticHeader().getCsticMulti())
{
objectBuilderFormElem.add("List", buildMListJson(csticDat, "sap.m.ListMode.MultiSelect", inst, formEleVarName));
objectBuilderFormElem.add("type", "sap.m.List");
}
else
{
if (csticVal.length>4)
{
objectBuilderFormElem.add("Select", buildMSelectJson(csticDat, true, inst, formEleVarName));
objectBuilderFormElem.add("type", "sap.m.Select");
}
.
.
.
SSC_UI5_V2.controller.js (createUIControls function)
The createUIControls function will parse the Json object sent back from the SSCServlet response and build out the UI controls. e.g. - for each object in the "detailPages" json attribute it will create a new Page
if (object == "detailPages")
{
detailPages = data[object];
for ( var page in detailPages)
{
newDPage = new sap.m.Page(detailPages[page].properties);
For each form element we get the control type and build out the control as specified:
controlType = formElems[elem].type;
switch (controlType)
{
case "sap.m.List":
oCtrlList = new sap.m.List(formElems[elem].List.properties);
switch (formElems[elem].List.mode)
{
case "sap.m.ListMode.MultiSelect":
oCtrlList.setMode(sap.m.ListMode.MultiSelect);
break;
case "sap.m.ListMode.SingleSelect":
oCtrlList.setMode(sap.m.ListMode.SingleSelect);
break;
}
oCtrlListItems = formElems[elem].List.StandardListItem;
for (var listItem in oCtrlListItems)
{
oCtrlList.addItem(new sap.m.StandardListItem(oCtrlListItems[listItem].properties));
}
oCtrlList.attachSelect(sap.ui.controller("ssc_ui5_v2.SSC_UI5_V2").onSelect);
oFormElement.addField(oCtrlList);
break;
case "sap.m.Select":
oCtrlSelect = new sap.m.Select(formElems[elem].Select.properties);
oCtrlSelectItems = formElems[elem].Select.Item;
for (var selectItem in oCtrlSelectItems)
{
oSelectItem = new sap.ui.core.Item(oCtrlSelectItems[selectItem].properties);
oCtrlSelect.addItem(oSelectItem);
if (oSelectItem.getText()== formElems[elem].Select.selectedVal)
{
oCtrlSelect.setSelectedItem(oSelectItem);
}
}
oCtrlSelect.attachChange(sap.ui.controller("ssc_ui5_v2.SSC_UI5_V2").onChange);
oFormElement.addField(oCtrlSelect);
break;
All the form elements are added to a FormContainer. The form container is added to the Form, which is added to the new Page and finally the new page is added to the SplitApp control.
oForm.addFormContainer(oFormContainer);
newDPage.addContent(oForm);
oSplit.addDetailPage(newDPage);
The master pages are built up in a similar fashion.
At this point the UI is built out with master pages and detail pages. The master pages control the navigation in the UI and are based on the instances of the config and the detail pages contain all the characteristics for each instance.
The next step was to handle UI events, e.g. a radio button is selected or a list item is selected from a List.
Each control (sap.m.Select, sap.m.List, sap.m.Input, etc...) will have a change or select event attached to it. These events will send a request to the SSCServlet again passing the change action along with the control that was changed and it's new value.
SSC_UI5_V2.controller.js (onSelect function)
onSelect : function(oEvent) {
var control = oEvent.getParameters().id;
var controlValue = oEvent.getParameters().listItem.getTitle();
$.ajax({
type : "POST",
url : "SSCServlet",
data : {
action: "change",
controlKey : control,
controlValueKey : controlValue
},
success : function(data) {
sap.ui.controller("ssc_ui5_v2.SSC_UI5_V2").updateUIControls(data);
},
error : function(data) {
alert("Failed:" + data);
}
});
},
Again the SSCServlet doGet method processes this request and passes the control and control value to the SSCClient uiChangeEvent method:
SSCClient (uiChangeEvent method)
Some of the main features of this are as follows:
ICsticData csticDat = null;
IDeltaBean csticBean = null;
csticDat.setCsticValues(newCsticVal);
csticBean = setCsticsValues(instId,cnfgSession.getRichConfigId(), false,new ICsticData[] { csticDat }, null, null,0);
HashMap<String, ICsticData[]> changedData = csticBean.getChangedCsticsAndValues();
ArrayList<IInstanceData> newInstances = csticBean.getNewInstances();
Similarly like we did when generating the json for the intial UI controls we can do the same with the new instances and the changed characteristics. This gets sent back to the onChange or onSelect functions of the controller js via the servlet response. The updateUIControls function is called which parses the json and sets the new values and/or creates the new instance pages and controls.
SSC_UI5_V2.controller.js (updateUIControls function)
The below demonstrates how the new values are set.
for ( var cstic in csticData)
{
control = sap.ui.getCore().byId(csticData[cstic].CsticName);
var controlType = control.getMetadata().getName();
switch (controlType)
{
case "sap.m.List":
var allItems = control.getItems();
var newCsticValues = csticData[cstic].csticValues;
var newCsticValue = null;
var newCsticValueAssigned = null;
for ( var csticVal in newCsticValues)
{
newCsticValue = newCsticValues[csticVal].CsticValue;
newCsticValueAssigned = newCsticValues[csticVal].CsticValueAssigned;
for ( var item in allItems)
{
if (allItems[item].getTitle() == newCsticValue)
{
if (newCsticValueAssigned)
{
// check if already selected. if not already selected then select it
if (!allItems[item].getSelected())
{
allItems[item].setSelected(true);
}
}
else
{
// check if already selected. if already selected then deselect it
if (allItems[item].getSelected())
{
allItems[item].setSelected(false);
}
}
}
}
}
break;
case "sap.m.Select":
var allItems = control.getItems();
var newCsticValues = csticData[cstic].csticValues;
var newCsticValue = null;
var newCsticValueAssigned = null;
for ( var csticVal in newCsticValues)
{
newCsticValue = newCsticValues[csticVal].CsticValue;
newCsticValueAssigned = newCsticValues[csticVal].CsticValueAssigned;
for ( var item in allItems)
{
if (allItems[item].getText() == newCsticValue && newCsticValueAssigned)
{
control.setSelectedItem(allItems[item]);
break;
}
}
}
break;
To give a flavor of what the knowledge base looks like I have included a few screenshots from the SSC Testing tool:
Note: once the Base Model VNX5200 is selected the engine defaults the Platform configuration and the Rack Selection characteristics. It also creates a new Drive Line instance.
And here are the screenshots of the UI5 frontend. Notice the master page on the left with the list of instances and the details on the right with the characteristics of the instances.
When Summary Page is selected it navigates to a new master page with a list of all the child instances under the Summary page instance. The navigation button at the top left allows navigation back to the initial master page.
In the VNX Series Solution select VNX5200 for the Base Model
The Platform Configuration and Rack Selection are defaulted as indicated below
A new Drive Line instance is added to the Storage Components. As Storage Components previously did not have any child instances a new master page is created.
Conclusion
To conclude, the hardest part of the above was figuring out the SSC APIs. Once I was able to pull out the instances and instance characteristics it was relatively straight forward to build up the json object, send it back the UI5 controller and build out the UI from that. The above is just a basic demo and is executed on a local Tomcat instance but gives an idea of what can be done with SAPUI5 as a frontend for SAP Solution Sales Configuration.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
5 | |
5 | |
4 | |
4 | |
4 | |
4 | |
4 | |
4 | |
3 | |
3 |