SAP web services and Flex

The company I work for uses SAP for their CRM (Customer Relations Management) solution.  We were on Oracle, but after being bought out by ze Germans, we had to switch to SAP.  The idea was to integrate the German offices with the American offices. That never happened and we ended up splitting off from the Germans this year.  However, SAP is still with us.  My boss requested that I create some sort of application to check if the web services called by our mobile CRM application, Service Data Automation (SDA), (created by me) were working as part of an overall systems check method.  The logical answer is, if the requests aren’t going through, then there is something wrong with the web services.  But, regardless of the obvious, it would be a good opportunity to show what Flex can do.  And if I can get the connections setup in Flex, then it would be a step closer to building a BlackBerry PlayBook or QNX enabled handheld device option using Flex.

The web services called by the SDA application actually use a middle-tier .Net platform (which I didn’t create) .  Since those that did create it and maintain it are no longer with the company, I have to figure out what each of the web services are doing.  I had created a Flex application earlier that connected to the SAP web services, but it is different than the web services used by SDA.  Luckily, I wasn’t the only one to go down this road.  At Computers Should Be Less Friendly, the author gives a quick an dirty example on how to connect to a SAP web service.  The only thing is doesn’t cover is the certification of the user.  For SDA, we created a generic user whose password never expires in order to process web services calls.  The web services still check to see if the person making the call is a valid user, so we have to pass in that identification as well.  But, by having this user, we only need one log-in and it won’t expire every 30 days (ugggh!!).

The normal connection string contains the username and password.  Usually I would store the user and password in order to switch the values when pointing to different instances.  It would look something like this.


http://crmurl.de:8033/sap/bc/srt/rfc/sap/ZCRM_DATA_WS?sap-client=sapClient&wsdl=1.0&style=rpc_enc&sap-user=" + sapUser + "&sap-password=" + sapPassword;

To add the user, password and client information, you need to add header information to the web service.


var sapWS:WebService = new WebService();

sapWS.headers["Authorization"] = "Basic " + encoder.toString();
sapWS.headers[ "sap-user" ] = sapUser;
sapWS.headers[ "sap-password" ] = sapPassword;
sapWS.headers[ "sap-client" ] = sapClient;

For my web services check, I made the web service dynamic by using a switch to determine the end point and uri of the web service.  On the display, I setup a ViewStack that contained a form for entering in parameters and a button to kick off the web service.  The button pointed to the function that built the web service and provides the case value.


switch (wsCheckName) {
case "PartLookup":
sapWS.wsdl = serverStartURL + partsLookup_WSDL + serverEndURL;
sapWS.addEventListener(LoadEvent.LOAD, partsLookup_load);
sapWS.addEventListener(ResultEvent.RESULT, partsLookup_results);
break;
case "InventoryLookup":
sapWS.wsdl = serverStartURL + inventoryLookup_WSDL + serverEndURL;
sapWS.addEventListener(LoadEvent.LOAD, inventoryLookup_load);
sapWS.addEventListener(ResultEvent.RESULT, inventoryLookup_results);
break;

}

The one thing to keep an eye out for is to verify if the web service is the same as the end point.  I believe that normally, this would be the case.  However, the person who created our custom web services in SAP apparently didn’t keep the naming convention the same.  So, I couldn’t pass the WSDL parameter (i.e. inventoryLookup_WSDL = Z_CRM_SDA_MAT_INV) in as the Operation call, because it was different.


<wsdl:portType name="Z_CRM_SDA_MAT_INV"><wsdl:operation name="Z_CRM_SDA_MAT_INV_DISPLAY"><wsdl:input message="tns:Z_CRM_SDA_MAT_INV_DISPLAY"/><wsdl:output message="tns:Z_CRM_SDA_MAT_INV_DISPLAYResponse"/></wsdl:operation></wsdl:portType>

Luckily, it is just a matter of pulling up the WSDL and finding the operation name and using that in the LoadEvent function.


var op:AbstractOperation = sapWS.getOperation(inventoryLookup_op);

var bapiInput:Object = new Object();
bapiInput.INVENTORY_DATA = new Array();;

bapiInput.R3RFCNAME = new Array();
bapiInput.R3RFCNAME.push(R3Destination);
bapiInput.LOCATION_DATA = new Array();
var locationRow:Object = new Object();
locationRow.WERKS = inventoryForm.ilWharehouse.text;
locationRow.LGORT = inventoryForm.ilBin.text;
locationRow.ALWAYS_SEND = "1"
bapiInput.LOCATION_DATA.push(locationRow);

bapiInput.MESG_T = new Array();
bapiInput.MESG_T.push();
bapiInput.MATNR = new Array();
bapiInput.MATNR.push(inventoryForm.ilPartNumber.text);
bapiInput.QUANTITY = new Array();
bapiInput.QUANTITY.push("1");

The biggest problem right now is building the tables and passing in the correct values to the proper columns.  WERKS?  What the heck is that?  I think it’s the Wharehouse and LGORT is the Location.

UPDATE

It seems that the header credentials never really worked.  The only thing that seems to work is putting the sap-user, sap-password and sap_client onto the end of the WSDL URL.  And, after figuring out that worked for the first part of the web service call, I tweaked and tested and tweaked and test until I cam across the obvious (not so to me at the start) solution that those values are also required in the endPoint URI.  Before, I kept the WSDL and endPoint URL/URI separate thinking that the extra information wasn’t need in the endPoint.  Well, it is now obvious to me that it is needed.  So, forget the headers part, it doesn’t seem to work, but the URL values works like a charm.

For each case, I added a second line to set the endpointURI.  It is exactly the same as the wsdl.  I also added the encodeURI function as well.


sapWS.wsdl = encodeURI(serverStartURL + partsLookup_WSDL + serverEndURL);
sapWS.endpointURI = encodeURI(serverStartURL + partsLookup_WSDL + serverEndURL);

For the serverEndURL, I make sure I have the sap-user, sap-client and sap-password.  I created a couple of new parameters to hold the current password and client based on which instance I want the application to point to (Dev or Test).


serverEndURL = "?wsdl=1.1&style=rpc_enc"
+ "&sap-user=" + sapUser
+ "&sap-password=" + currentPassword
+ "&sap-client=" + currentClient;

About DeanLogic
Dean has been playing around with programming ever since his family got an IBM PC back in the early 80's. Things have changed since BASICA and Dean has dabbled in HTML, JavaScript, Action Script, Flex, Flash, PHP, C#, C++, J2ME and SQL. On this site Dean likes to share his adventures in coding. And since programming isn't enough of a time killer, Dean has also picked up the hobby of short film creation.

About DeanLogic

Dean has been playing around with programming ever since his family got an IBM PC back in the early 80's. Things have changed since BASICA and Dean has dabbled in HTML, JavaScript, Action Script, Flex, Flash, PHP, C#, C++, J2ME and SQL. On this site Dean likes to share his adventures in coding. And since programming isn't enough of a time killer, Dean has also picked up the hobby of short film creation.