...by Daniel Szego
quote
"On a long enough timeline we will all become Satoshi Nakamoto.."
Daniel Szego

Thursday, July 10, 2014

Connecting SAP employee information with SharePoint user profile services - Part 1

Some companies use both SharePoint and some kind of a SAP system and in the internal IT landscape. The problem is very often that some user specific information is stored both in AD and imported to SharePoint with the help of user profile sync and the same data is stored and managed on the SAP side as well. This article demonstrates how you can directly integrate SAP employee specific data with SharePoint.
Reaching the goal we need to do actually three major steps:
  • STEP 1. Pull out the information from SAP.

  • STEP 2. Integrate with BCS
  • STEP 3. Configure BCS with the user profile sync of SharePoint.

  • STEP 1: you have many options to do that depending on what kind of an environment you have and what kind of a programming experience. Some of the possible examples:
    •  
     If you have Netweaver than you can get your data with the help different web services and put it to    a BCS class event with SharePoint designer.
  • Without Netweaver you should use some kind of a connector, like the SAP .NET Connector (https://help.sap.com/saphelp_nw04/helpdata/de/e9/23c80d66d08c4c8c044a3ea11ca90f/content.htm) or the ERP connect from Theobald (http://theobald-software.com/de/index.htm),
  • If you are brave enough and do not afraid of deep dive dll hacking then you can use the original communication interface from SAP: librf32.dll.


  • In this scenario we use the SAP.NET connector, so let we create for the first run a simple console application that read out the information from SAP:
    1.  Create a Visual Studio project, a console application for the first run, name it like Test and add a reference to the SAP .NET connector dlls (rscp4n.dll, sapnco.dll, sapnco_utils.dll).
    2 Set the SAP configuration parameters by implementing the IDestinationConfiguration​ interface  and set the ​SAP connection parameters like AppServerHost, SysteNumber and a user credential to communicate. 
    public class SAPSystemConnect : IDestinationConfiguration
        {
            bool IDestinationConfiguration.ChangeEventsSupported()
            {
                return true;
            }
            public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;
            RfcConfigParameters IDestinationConfiguration.GetParameters(string destinationName)
            {
                if ("XXX".Equals(destinationName))
                {
                    RfcConfigParameters parms = new RfcConfigParameters();
                    parms.Add(RfcConfigParameters.AppServerHost, "XXX");
                    parms.Add(RfcConfigParameters.SystemNumber, "XXX");
                    parms.Add(RfcConfigParameters.User, "XXX");
                    parms.Add(RfcConfigParameters.Password, "XXX");
                    parms.Add(RfcConfigParameters.Client, "XXX");
                    parms.Add(RfcConfigParameters.Language, "XXX");
                    parms.Add(RfcConfigParameters.PoolSize, "XXX");
                    parms.Add(RfcConfigParameters.MaxPoolSize, "XXX");
                    parms.Add(RfcConfigParameters.IdleTimeout, "XXX");
    
                    return parms;
                }
                return null;
            }
        }

     3. create an Employee wrapper class to capture the required properites of employee. We use Name, Organisation and Job as simple strings in this example. Having finished implement a GetAllEmployee function to read out infromation from SAP and create a list of Employee wrapper objects.
    public class Employee
        {
            // BAPI_EMPLOYEE_GETLIST - ENAME
            public string Name;
    
            // BAPI_EMPLOYEE_GETLIST - ORG_TEXT
            public string Organisation;
    
            // BAPI_EMPLOYEE_GETLIST - JOB_TEXT
            public string Job;
    
            public static List<Employee> getAllEmployees(RfcDestination destination)
            {
                List<Employee> ret = new List<Employee>();
    
                RfcRepository repo = destination.Repository;
                IRfcFunction employeeList = repo.CreateFunction("BAPI_EMPLOYEE_GETLIST");
                employeeList.SetValue("ORG_SEARK", "*");
                employeeList.SetValue("JOB_SEARK", "*");
                employeeList.SetValue("SUR_NAME_SEARK", "*");
                employeeList.SetValue("LST_NAME_SEARK", "*");
    
                employeeList.Invoke(destination);
                IRfcTable employees = employeeList.GetTable("EMPLOYEE_LIST");
    
                for (int cuIndex = 0; cuIndex < employees.RowCount; cuIndex++)
                {
                    employees.CurrentIndex = cuIndex;
                    Employee emp = new Employee();
                    emp.Name = employees.GetString("ENAME");
                    emp.Organisation = employees.GetString("ORG_TEXT");
                    emp.Job = employees.GetString("JOB_TEXT");
    
                    ret.Add(emp);
                }
                return ret;
            }
        }
    The prvious code expects a preconfigured destination that represents the SAP connection. Then calls the BAPI_EMPLOYEE_GETLIST BAPI with the some imput parameter settings (practicaly setting * as wildchar for the ORG_SEARK, JOB_SEARK, SUR_NAME_SEARK and LST_NAME_SEARK input parameters). 
    After calling the BAPI the EMPLOYEE_LIST result table is iterated and the necessary output values are read out. Anyway if you feel unconfident with the parameter names of the BAPI, then start SAP GUI, start the se37 transaction and test the BAPI with different input output variable conbinations. 
    3. Test the program from the console application by creating a  destination and calling getAllEmployees static procedure.
    
    
     static void Main(string[] args)
            {
                //test 
                Console.WriteLine("Start");
                SAPSystemConnect sapCfg = new SAPSystemConnect();
    
                RfcDestinationManager.RegisterDestinationConfiguration(sapCfg);
                RfcDestination rfcDest = RfcDestinationManager.GetDestination("K47");
    
    
                List<Employee> emps = Employee.getAllEmployees(rfcDest);
                foreach (Employee emp in emps)
                {
                    Console.WriteLine(string.Format("Employee {0} - {1} - {2}", emp.Name, emp.Job, emp.Organisation));
                }
                Console.WriteLine("End");
                Console.ReadLine();
            }

    Having finished, you should see a list of employee names and the regarding job and organisation information on the one hand as a result of the console application :



    On the other hand you can test if the data is actually the same in SAP, as an example by starting the se37 tranaction and calling the BAPI_EMPLOYEE_GETLIST with the same '*' input values : 



    In the next part of this blog, we integrate the exsiting project into a BCS Project and add to the SharePoint user profile sync.