...by Daniel Szego
quote
"On a long enough timeline we will all become Satoshi Nakamoto.."
Daniel Szego
Showing posts with label SAP. Show all posts
Showing posts with label SAP. Show all posts

Monday, January 11, 2016

Role of the customer knowledge in Software Industry

It is a pretty interesting question how does the whole IT industry look like if we consider the role of the customer knowledge. With other words, let we imagine that we have a customer that wants to order user or buy a certain software solution. One of the most interesting question how much the customer knows about the field or software that is being ordered. The following picture shows the rough conceptual model.




Figure 1. The role of customer knowledge in software industry.

Based on the model, the following typical cases can be distinguished.


Everything in details: If the customer knows everything about the software that is being ordered than the most typical way is to create a custom development with one of the classical software development methodology, like waterfall model, V or W model. The specification can be really 100% defined and documented, typically the development can be carried out by a remote development team as well, giving a good potential for offshoring or near-shoring.

Detailed concept: Most customers do not really have a detailed concept about how the software exactly look like, usually because of the missing experience in requirement analysis and software engineering.This provide a perfect way for agile development, having a strong and common communication with the customer, delivering early prototypes and gaining feedbacks from the customer apart from the pure specification. The typical solutions of the fields are methodologies like scrum or extreme programming.

Rough Concept: If the customer has got only a rough concept which software does she need, than the software delivery has to be much more agile. This can be realized in two ways, on the one hand there are some hyper-agile framework like K2 or Oracle AppBuilder that enables to change the environment pretty fast, having practically daily software delivery. On the other hand some software frameworks provides the possibility for a power user to build up applications on their own, like standard SharePoint or partly with a Nintex extension.

Detailed Business Know-How: If the customer has got a detailed business know-how, however she lacks of the IT or software concept and experience than ready products are the best solutions to be offered. Certainly there might be a possibility to set up a team with a business analyst as well, however in this situation the best idea is always to buy a ready software if there is one. If not, custom agile development with business analyst support can be evaluated as well.

Rough Business Know-How: Well that is a difficult situation. Let we imagine a customer that wants to buy a software but she lacks of the necessary software development skills and experience, hence her business know-how is not perfect either. That means that the customer needs both business consulting and software product or development. The other solution that the customer has to buy software solution which is de-facto best practice on the market, meaning that business best practice is actually hard-coded in the software itself. SAP is a leading to deliver such solutions.

Minimal Business Know-How: Well in this case, the customer needs to have both pretty much business consulting and a software solution as well. In this case, the optimal way if the business consulting analysis precedes the software evaluation and the choosen product is actually based on the pure business consulting. 

Sunday, November 1, 2015

Integrating MatchPoint with SAP (a step by step initial approach)

Integrating MatchPoint with SAP is pretty easy as a first approach. You have to do two things: 
- get an SAP connector to read out some information from SAP
- implement a data provider in MatchPoint with the SAP adapter to use the imported data.

So let we have the steps in details:
STEP 1. Let we assume that we want to see the controlling area names from SAP controlling:


Figure 1. List of controlling areas in SAP.

STEP 2. Let we create some integration with SAP, there are many ways of doing that ranging from BAPI calls to web service integration through Netweaver. I m choosing the most simple SAP .NET connector
STEP 3. Let we create a Visual Studio project console application and reference the SAP.NET connecor dlls: sapnco.dll and sapnco_utils.dll. I am probably using a little bit old version, because for me it was working only with .NET framework 4.0 and not with 4.5 or higher. 
STEP 4. Let we create a basic connection class named SAPSystemConnectMP to store the SAP connection information, like port, system number, access information...:

    public class SAPSystemConnectMP : IDestinationConfiguration
    {
        bool IDestinationConfiguration.ChangeEventsSupported()
        {
            return true;
        }
        public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;
        RfcConfigParameters IDestinationConfiguration.GetParameters(string destinationName)
        {
            if ("K47".Equals(destinationName))
            {
                RfcConfigParameters parms = new RfcConfigParameters();
                parms.Add(RfcConfigParameters.AppServerHost, "192.168.137.238");
                parms.Add(RfcConfigParameters.SystemNumber, "00");
                parms.Add(RfcConfigParameters.User, "root");
                parms.Add(RfcConfigParameters.Password, "***");
                parms.Add(RfcConfigParameters.Client, "800");
                parms.Add(RfcConfigParameters.Language, "DE");
                parms.Add(RfcConfigParameters.PoolSize, "50");
                parms.Add(RfcConfigParameters.MaxPoolSize, "100");
                parms.Add(RfcConfigParameters.IdleTimeout, "6000");

                return parms;
            }
            return null;
        }
    }

STEP 5. Let we create a wrapper class for the ControllingArea, with the static setup procedure that calls the BAPI_CONTROLLINGAREA_GETLIST Bapi and from the result takes the CONTROLLINGAREA_LIST table and lists all controlling area name and code.

   public class ControllingArea
    {
        public string ControllingAreaCode;
        public string ControllingAreaName;

        public static List<ControllingArea> getAllControllingAreas(RfcDestination destination)
        {
            List<ControllingArea> ret = new List<ControllingArea>();

            RfcRepository repo = destination.Repository;
            IRfcFunction controllingAreaList = repo.CreateFunction("BAPI_CONTROLLINGAREA_GETLIST");

            controllingAreaList.Invoke(destination);
            IRfcTable controllingAreas = controllingAreaList.GetTable("CONTROLLINGAREA_LIST");

            for (int cuIndex = 0; cuIndex < controllingAreas.RowCount; cuIndex++)
            {
                controllingAreas.CurrentIndex = cuIndex;
                ControllingArea area = new ControllingArea();
                area.ControllingAreaCode = controllingAreas.GetString("CO_AREA");
                area.ControllingAreaName = controllingAreas.GetString("Name");
                ret.Add(area);
            }
            return ret;
        }
    }

STEP 6. Let we implement a MatchPoint Data Provider, of course after referencing the MathcPoint dlls, possibly from the development kit : Colygon.MatchPoint, Colygon.MatchPoint.Server and Colygon.MatchPoint.Snow.

    [Serializable]
    public class SAPDataProvider : BaseDataProvider
    {
        public string[] List;

        public override BaseDataProviderInstance CreateInstance(string cacheKey, IEnumerable<string> columnNames)
        {
            return new SAPDataProviderInstance(cacheKey, this, columnNames);
        }
    }

STEP 7. Last but not least let we implement a data provider instance. Perhaps the most important part of the instance is the GetInternalData function. It opens an SAP connection through the configured connection class, queries all the controlling areas and transforms the names to a simple string list.

   public class SAPDataProviderInstance : BaseDataProviderInstance
    {
 private readonly SAPDataProvider provider;

 public SAPDataProviderInstance(string cacheKey, SAPDataProvider provider, IEnumerable<string> columnNames) : base(cacheKey, columnNames)
 {
  this.provider = provider;
 }

 protected override CachePolicy CachePolicy
 {
  get { return new CachePolicy(CacheGranularity.NoCache, 0); }
 }

 protected override IEnumerable<object> GetInternalData()
 {
            SAPSystemConnectMP sapCfg = new SAPSystemConnectMP();
            try
            {
                RfcDestinationManager.RegisterDestinationConfiguration(sapCfg);
            }
            catch(Exception ex){}
            
            RfcDestination rfcDest = RfcDestinationManager.GetDestination("K47");
            List<ControllingArea> cAreas = ControllingArea.getAllControllingAreas(rfcDest);
            List<string> res = new List<string>();
            foreach (ControllingArea area in cAreas)
            {
                res.Add(area.ControllingAreaName);
            }

            provider.List = res.ToArray<string>();
            return provider.List;
 }

 public override string GetCacheToken()
 {
  return null;
 }
    }

STEP 8. Having compiled,we have to registrate everything in GAC (the SAP connector DLLs as well) and we have to registrate the DataProvider in the matchpoint configuration as an external assembly and of course IISreset at the end.


Figure 2. MatchPoint Configuration.

STEP 9. If everything went well, you have to see the new dataprovider in your system. So as an test we can create a composite WebPart setting the SAPDataProvier and showing with a pattern transformer the DataItem.


Figure 3. Composite WebPart configuration.

STEP 10. If everything wen well, you should see the list of Controlling Areas directly from SAP.


Figure 4. Controlling area information in MatchPoint from SAP.

Thursday, July 10, 2014

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

In the first part of the blog, we have seen how the basic connection with SAP can be set up :
Connecting-SAP-employee-information with SharePoint user profile services Part 1 
Now let we see how the BCS side can be configured :
STEP 2 : Creating BCS. 
1. Create an empty SharePoint 2013 Project in the same solution add a Business Data Connectivity Model to the Project and do not forget to refernce the project of the old command promt application, fo course ony after that you set the project output type to class library :



2. configure in the package, that both the solution dlls and the SAP Connector dlls are deliverd to the GAC (rscp4n.dll, sapnco.dll, sapnco_utils.dll)
3. Add a static Connection class to have a reference on the connection itself :​
    public static class Connection
    {
        public static RfcDestination rfcDest = null;

        static Connection()
        {
            SAPSystemConnect sapCfg = new SAPSystemConnect();
            RfcDestinationManager.RegisterDestinationConfiguration(sapCfg);
            rfcDest = RfcDestinationManager.GetDestination("XXX");
        }
    }​

4. Update the employee to cover the basic properties.
   public partial class EmployeeEntity
    {
        public EmployeeEntity(Employee emp)
        {
            this.Job = emp.Job;
            this.Name = emp.Name;
            this.Organisation = emp.Organisation;
            this.PeronalNr = emp.PeronalNr;
        }
        public EmployeeEntity()
        {
        }
        private string _PeronalNr;
        private string _Name;
        private string _Organisation;
        private string _Job;
        // BAPI_EMPLOYEE_GETLIST - ENAME
        public string Name
        {
            get
            {
                return _Name;
            }
            set
            {
                _Name = value;
            }
        }
        // BAPI_EMPLOYEE_GETLIST - ORG_TEXT
        public string Organisation
        {
            get
            {
                return _Organisation;
            }
            set
            {
                _Organisation = value;
            }
        }

        // BAPI_EMPLOYEE_GETLIST - JOB_TEXT
        public string Job
        {
            get
            {
                return _Job;
            }
            set
            {
                _Job = value;
            }
        }
    }
}
5. Implement the finder and specific finder methods to get the employee infromation:
      public static EmployeeEntity ReadItem(string Name)
        {
            Employee e = new Employee();
            EmployeeEntity ret = new EmployeeEntity(e);
            // get all Basic employee information from SAP

            List<Employee> emps = Employee.getAllEmployees(Connection.rfcDest);
            foreach (Employee emp in emps)
            {
                if(Name.ToLower().Contains(emp.Name.ToLower()))
                {
                    ret = new EmployeeEntity(emp);
                }
                if (emp.Name.ToLower().Contains(Name.ToLower()))
                {
                    ret = new EmployeeEntity(emp);
                }
                if (emp.Name.ToLower().Equals(Name.ToLower()))
                {
                    ret = new EmployeeEntity(emp);
                }
            }
            return ret;
        }
 
       public static IEnumerable<EmployeeEntity> ReadList()
        {
            List<EmployeeEntity> ret = new List<EmployeeEntity>();
            // get all Basic employee information from SAP

            List<Employee> emps = Employee.getAllEmployees(Connection.rfcDest);
            foreach (Employee emp in emps)
            {
                ret.Add(new EmployeeEntity(emp));
            }
            return ret;
        }

In this example specific finder is simply the name of an employee, that is pretty much general configured to match. We certainly use the Employee wrapper class that was implemented in the first part of this blog to capture the SAP information, 
6. Configure BCS either with the help of the editor or directly the xml to contain the properties and the Name property as an indexer :


7. After building and deploying, you can test if it works as well, like creating an external list in one of the sites and populating SAPUserProfil EmployeeEntity external type (do not forget giving rights to the external content type at the central administration) :



STEP 3: So, last but not least, the external content type has to be configured in the user profil sync: 
1. In the User Profile Service Application by Configure Syncronisation Connection create a new connection from the type Business Data Connectivity, and configure a matching based on the AccountName or PreferedName (basicaly the specific finder is pretty flexible configured, so all of these conbinations should work). In this example we already have one connection to syncronise the basic accounts and user related information from Active Directory.



2. Create two new properties in the user profile service application for SAP Job and SAP Organisation infromation (like SAPJob2 and SAPOrg2) and configure them to get the information from the newly configured syncronisation connection. 



3. Start a full User Profile syncronisation. 
4. Check if the user profile has got the information in the extended properties, like 


...

The end. 

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.

    Integrating K2 with SAP with K2 Connect

    So, it was a challenge to see how one can integrate K2 with SAP without K2 Connect. Now let we see ho the same process can be realised with the help of K2 Connect. Prerqisit : You need to have K2 Blackpearl, K2Smartforms, K2 Connect and an SAP System instlled not necesarily with Netweaver but with a Controlling module.So for the first run start Visual Studio, create a new K2 Project called K2ConnectTest and add a new K2 Connect Service Object called K2ConnectTest as well to the project :



    Having finished open in the VS Toolbox the K2 Service Object Designer and start a new filter criteria with "*costcenter*", after that choose the BAPI_COSTCENTER_GETLIST Bapi, tes it with the help of the K2 connect Test Cockpit at loading the interface then testing the interface firstly by adding 1000 default value to the controlling area and leaving the other values as default. 



    If everything succeed and you get the result value in the costcenter list. then add a new service, name it as Costcenter, to the Visual Studio project and add the BAPI_COSTCENTER_GETLIST to the Costcenter service :



    Having finished, build the VS projects and say Publish Service Object. If everything runs without error then open the Smart Object Service Tester application, the K2Connecttest should be published under K2 Connect Service :

    ​​

    As a next step create a Smart Object from the K2ConnectTest Smart Object Service. After that test it explicitely by adding 100 to the ControllingArea input field:



    As a last step create a View and a Smart Form from the Smart Object, for the first run just simple pressing the generate view button on the Smart Object. For the first run you will probably get an error message it is because the ControllingArea input field is not defined. So ​​open the autogenerated View, go to the Initialisation rule and set the ControllingArea to 1000 for the first run:



    If everything goes well, then after running either the form or directly the view you should get the information of the list of costcenters to the 1000 controlling area:



    Conclusions : 
    - You can integrate SAP with K2 with or without K2 connect. 
    - If you do it without K2 Connect you must have development skills in C# and some direct SAP know how. 
    - If you do it with K2 connect you do not necessarily must know to code or know how to handle SAP, however you have to have a strong IT Skill. 
    - If you do with K2 Connect, you have to pay the K2 connect license (as I know around 20k).
    - If you do  without K2 Connect, you have to have one connector (as I know Theobald is around 1k pro year, if you manage to hack lib32rfc.dll it is as I know free of charge)
    - There is no best solution, just an optimal for a certain customer :)

    The end.

    Integrating K2 with SAP (without K2 connect) - Part 2

    In the first part of this blog we set up the envrionment and started to implement the first class for a custom smart object serivce to make an integration with SAP. 
    Let we just continue with the rest of the coding. Let we just overwirte DescribeSchema : it implements which properties and which methods are published from the given smart object service :​

    public override string DescribeSchema()
    {
        //set base info 
        this.Service.Name = "SPEGSAPService";
        this.Service.MetaData.DisplayName = "SPEGSAPService";
        this.Service.MetaData.Description = "SPEGSAPService";

        //Create the service object, one to many 
        ServiceObject so = new ServiceObject();
        so.Name = "SPEGSAPServiceObject";
        so.MetaData.DisplayName = "SPEGSAPServiceObject";
        so.MetaData.DisplayName = "SPEGSAPServiceObject";
        so.Active = true;

        //Create field definition for CostCenter ID
        Property propertyCostCenterID = new Property();
        propertyCostCenterID.Name = "CostCenterID";
        propertyCostCenterID.MetaData.DisplayName = "CostCenterID";
        propertyCostCenterID.MetaData.Description = "CostCenterID";
        propertyCostCenterID.Type = "System.String";
        propertyCostCenterID.SoType = SoType.Text;
        so.Properties.Add(propertyCostCenterID);

        //Create field definition Cost Center Name
        Property propertyCostCenterName = new Property();
        propertyCostCenterName.Name = "CostCenterName";
        propertyCostCenterName.MetaData.DisplayName = "CostCenterName";
        propertyCostCenterName.MetaData.Description = "CostCenterName";
        propertyCostCenterName.Type = "System.String";
        propertyCostCenterName.SoType = SoType.Text;
        so.Properties.Add(propertyCostCenterName);

        //Create method
        Method method = new Method();
        method.Name = "Load";
        method.MetaData.DisplayName = "Load";
        method.MetaData.Description = "Load custom service data";
        method.Type = MethodType.List;
        method.ReturnProperties.Add(propertyCostCenterID);
        method.ReturnProperties.Add(propertyCostCenterName);
        so.Methods.Add(method);
        this.Service.ServiceObjects.Add(so);

        return base.DescribeSchema();
    }

    In the previous example we achived that the smart object instances of this type will publish two properties, named CostCenterID and CostCenterName, and there is gonna be one method, a list type, that reads out of list of these properties.   Let we now override the main Execute method that is called when any of the methods of the smartobject is called :
    public override void Execute()
    {
            foreach (ServiceObject so in Service.ServiceObjects)
            {
                switch (so.Name)
                {
                    case "SPEGSAPServiceObject":
                        foreach (Method method in so.Methods)
                        {
                            switch (method.Type)
                            {
                                case MethodType.List:
                                    ReadSPEGSAPService(so, method);
                                    break;
                            }
                        }      
                        break;
                }
            }
    }

    The previous method simply iterates over possible service objects and methods, and chooses practicaly the only one that has been implemented and calls ReadSPEGSAPServices to read out the data from SAP. This function is acually responsible for for the SAP connection, it initiates a R3 connection with a given connection string to an SAP system. After that it creates a function to the BAPI_COSTCENTER_GETLIST BAPI; input parameters are set; with function.Execute() is the BAPI carried out; at the end the COSTCENTER_LIST table is iterated and costcenter code and name are read out.  Putting the result back is realised with the help of the ServiceObject property and table functions. Please note that BAPI structure is the easiest to discover with the help of the se37 transaction, described at the first part of this blog.

    private void ReadSPEGSAPService(ServiceObject so, Method method)
    {
        string connstring = "ASHOST=... SYSNR=... USER=... PASSWD=... LANG=... CLIENT=...";
        var connection = new R3Connection(connstring);

        try
        {
            connection.Open();

            var function = connection.CreateFunction("BAPI_COSTCENTER_GETLIST");
            function.Exports["CONTROLLINGAREA"].ParamValue = "1000";
            function.Execute();
            var table = function.Tables["COSTCENTER_LIST"];

            if (table != null || table.Rows.Count > 0)
            {
                var c = 0;
                so.Properties.InitResultTable();
                foreach (RFCStructure row in table.Rows)
                {
                    if (c++ > 100)
                        break;

                    var costcenterid = (string)row["COSTCENTER"];
                    var costcentername = (string)row["COCNTR_TXT"];

                    so.Properties["CostCenterName"].Value = costcentername;
                    so.Properties["CostCenterID"].Value = costcenterid;
                    so.Properties.BindPropertiesToResultTable();
                }
            }
        }
        finally
        {
            if (connection != null && connection.Ping())
                connection.Close();
        }
    }

    Last but not least, overwrite the Extend method with an empty procedure, than compile and build the project. 
     override void Extend()
    {
    }
    STEP 4: Deployment. To deploy the project firstly copy the created dll and the ERPConnect45.dll as well to the folder of the service type dlls.  C:\Program Files (x86)\K2 blackpearl\ServiceBroker.Open SmartObject Service Tester (located in C:\Program Files (x86)\K2 blackpearl\Bin) and register the new service type.



    Create a new service instance from the service type; here you can set the ControlAreaNumber configuration property as it was shown on the screenshot in the first part of this blog. Having created the instance create some SmartObject from the type and test them with the helo of SmartObject Service Tester. If everything went fine, you should get a list of costcenter IDs and Names after pressing execute on the test window. Please note that if something went wrong then the easiest way to find a bug is with the help of the Visual Studio Debugger. You should Attach to the K2HostServer.exe and you can debug your code.



    If everything works, the smartobject that you created is ready for being used either in K2 workflow or in Smartforms. As an example the following screenshot demonstrates the simpleest Smartform generated to show the result.



    The end.

    Integrating K2 with SAP (without K2 connect) - Part 1

    ​So, ever wanted to integrate your K2 envrionment with SAP ? Well actually you have some possibilities. The first option is certainly K2 connect that is meant to provide a tool to easily configure data from SAP as smart objects. If you do not have or you do not want to use K2 connect, one option is certainly to make an integration via the web services of SAP integrating either with Endpoints WCF or Endpoints Web services smart object service.
    However you need to have a version from SAP Netweaver and publish some web services. So what happens, if you do not want to use K2 connect and you want to do the integration without web services and netweaver for some reasons, with the help of the old fashioned BAPI integration.  Well, it is possible, but you need deveopers. We demonstrate in the followings how :
    Firtly you need of course Visual Studio 2010 or 2012 and an SAP connector. 
    Among the other you can consider the following options: 
    - ERP Connect from Theobald : 
      http://theobald-software.com/en/erpconnect-productinfo.htm
      You can download a trial version but buying a final version is also not so expensive. 
    - SAP .Net connector :
      You can download the product from the SAP Marketplace. Unfortunately if you are not an SAP Partner,     it might be a little bit difficult.
    - Reverse engineering librfc32.dll :
      At installing the SAP GUI, a librfc32.dll is installed as well, theoreticaly it is capable of doing the SAP     connection, I mean of course if you are good ar reverse engineering. 

    In the following, we use ERP Connect from Theobald. 
    In the following use case we want read out a list of cost center names and cost center codes for a given controlling area code.  

    STEP 1: Choose and test a BAPI.
    Reading out cost cetner information is possible with the help of the BAPI_COSTCENTER_GETLIST Bapi. 
    You can directly test the BAPI by starting se37 transaction.


    with Display you can see the code and with "Test/Execute" you can diretly test the BAPI with different input parameter conbination. 




    STEP 2: Open Visual studio and create an initial project producing a class library on framework 4.5.



    Three important things however: 
    - Install ERP Connect on the development platform and do not forget to reference the ERPConnect45.dll
    - Add reference for the SourceCode.SmartObjects.Services.ServiceSDK.dll, it can be found under C:\Program Files (x86)\K2 blackpearl\Host Server\Bin\
    - Install the librfc32.dll exactly in a way as it is described in (otherwise you well get hot error messages in the future):
    Configure librfc32.dll on an 64 bit environment​
    STEP 3: Make code. 
    The major point gonna be to implement a custom smartobject service, you can have a basic approcach for example from the following link:
    create a descendent class from the ServiceAssemplyBase : it is the parent class for all custom SmartObject serv​ice.

    public class SPEGServiceBrokerClass : ServiceAssemblyBase
     {
         public SPEGServiceBrokerClass()
         {
         }
    override the GetConfigSession procedure and set the properties that you need : ConfigSession contains the possible parameters of SmartObject Service Instance that you can set for instance  in the SmartObject Services Tester Program. See the following screenshot :



    In our example we only add on such a property that is the ControllingAreaNumber

    private ServiceConfiguration _serviceConfiguration;

    public ServiceConfiguration ServiceConfig
    {
        get { return _serviceConfiguration; }
        set { _serviceConfiguration = value; }
    }

    // configuring the basic properties
    public override string GetConfigSection()
    {
        this.Service.ServiceConfiguration.Add("ControllingAreaNumber"truestring.Empty);
        return base.GetConfigSection();
    }

    ​To be continued in Part 2