How to import Employee Master Data to S/4HANA Cloud – Part II
Introduction
On the second part of this blog we will see a way to automate Employee Import process using web-services.
The automation will be provided using SAP Cloud Platform Integration Services and API Replicate Basic Master Data for Workforce from External HR Systems.
On our sample scenario a file produced by an external systems is placed in a folder of a SFTP server containing Employee Master Data. CPI will consume this file, transform, map it and call S/4HANA Cloud API to complete employee creation.
Because we are working with a synchronous API integration flow will also take care of the response and send it back to the SFTP folder to be consumed by the external system.
The information contained in the response will allow the external system to update already created employees.
S/4HANA Cloud configuration
First step is to correctly identify our API and get documentation. To do so we go to api.sap.com and search for our API. The full name is Replicate Basic Master Data for Workforce from External HR Systems.
In the API details we find what are scope items using this API and what communication arrangement needs to be activated in S/4HANA Cloud to make this API active.
You can also find relevant business information by following the Business Documentation link. You can also find information about field data types and field cardinality can be found on API Reference tab.
Logon to your S/4HANA Cloud system and using Communication Arrangement application create a communication arrangement based on communication scenario SAP_COM_0301. During this process you will also need to create a communication system (representing the sending system) and communication user to set execute basic authentication. From list of inbound services download the WSDL file for the API. This WSDL file will contain all required information i.e. endpoints.
After communication arrangement setup API is available and can be tested using tools like SOAP UI.
CPI – Integration flow design and configuration
On CPI we create an integration flow containing 3 communication components:
- SFTP-out – representing folder to read Employee master data coming from the external system
- S4HC – representing S/4HANA Cloud system
- SFTP-in representing folder to write employee master data replication results
Step 0: Sender adapter configuration
Setup your sender adapter configuration with the details of your SFTP server. If using basic authentication you need to create user credentials first before you can finish the SFTP adapter configuration. Also for SFTP connections remember you need to add your SFTP fingerprint to the know_hosts file hosted on your CPI tenant.
You can test and get your SFTP server fingerprint by using the connectivity tests in CPI.
Step 1: CSV to XML converter
To convert from the CSV file to XML CPI offers a converter.
Configuration of the CSV to XML converter requires an XSD to define the output XML.
Documentation for this converter can be found here.
<?xml version="1.0" encoding="UTF-8"?>
<schema targetNamespace="http://www.example.org/basic_employee_import" elementFormDefault="unqualified" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.example.org/basic_employee_import">
<complexType name="list_employee_type">
<sequence>
<element name="employee" type="tns:employee_type"
maxOccurs="unbounded" minOccurs="1">
</element>
</sequence>
</complexType>
<complexType name="employee_type">
<sequence>
<element name="PersonUUID" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="PersonExternalID" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="Username" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="BusinessPartnerRole" type="string"
maxOccurs="1" minOccurs="0">
</element>
<element name="Supplier" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="FirstName" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="LastName" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="PersonFullName" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="GenderCode" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="EmailAddress" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="PhoneNumber" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="MobilePhoneNumber" type="string"
maxOccurs="1" minOccurs="0">
</element>
<element name="Language" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="CompanyCode" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="CostCenter" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="StartDate" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="EndDate" type="string" maxOccurs="1"
minOccurs="0">
</element>
<element name="PersonWorkAgreementUUID" type="string"
maxOccurs="1" minOccurs="0">
</element>
<element name="PersonWorkAgreementExternalID" type="string" maxOccurs="1" minOccurs="0"></element>
</sequence>
</complexType>
<element name="root" type="tns:list_employee_type"></element>
</schema>
Step 2: Design a mapping from previous XSD to the request message of Replicate Basic Master Data for Workforce from External HR Systems API. The WSDL file needs to be uploaded to your Integration flow before you can assign to the target structure of the mapping.
Step 3: Call service
Use Call service and setup SOAP adapter receiver to configure the connection to S/4HANA Cloud API.
Step 4: Process response and transform response to CSV.
Using a Groovy script convert the SOAP API response back to CSV.
import com.sap.gateway.ip.core.customdev.util.Message;
import java.lang.StringBuilder;
import javax.xml.namespace.NamespaceContext;
import java.util.HashMap;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Document;
class localNamespaceContext implements NamespaceContext {
public String getNamespaceURI(String prefix) {
if (prefix.equals("n0")) return "http://sap.com/xi/PASEIN";
}
public String getPrefix(String uri) {
throw new UnsupportedOperationException();
}
public Iterator getPrefixes(String uri) {
throw new UnsupportedOperationException();
}
}
def Message processData(Message message) {
def sourceDocument = message.getBody(org.w3c.dom.Document);
XPathFactory factory = XPathFactory.newInstance();
XPath xPath = factory.newXPath();
xPath.setNamespaceContext(new localNamespaceContext());
StringBuilder finalMessage = new StringBuilder();
for (Node logNode : (NodeList) xPath.evaluate("//n0:WorkforcePersonMasterDataReplicationResponse_sync/WorkforcePersonMasterDataReplResponse", sourceDocument, XPathConstants.NODESET)) {
if(logNode!=null){
// Read ProcessStatusCode
Node processingStatusNode = ((Node) xPath.evaluate("./ProcessingStatusCode", logNode, XPathConstants.NODE));
String processingStatusValue = processingStatusNode.getTextContent();
int processingStatusValueInt = (Integer.valueOf(processingStatusValue)).intValue();
// Read PersonExternalID
Node personExternalIdNode = ((Node) xPath.evaluate("./PersonExternalID", logNode, XPathConstants.NODE));
String personExternalIdValue = personExternalIdNode.getTextContent();
// Read PersonUUID
Node personUUIDNode = ((Node) xPath.evaluate("./PersonUUID", logNode, XPathConstants.NODE));
String personPersonUUIDValue = personUUIDNode.getTextContent();
//process workAgreementNode
for (Node workAgreementNode : (NodeList) xPath.evaluate("./WorkforcePersonMasterDataReplConfLogMessage", logNode, XPathConstants.NODESET)) {
finalMessage.append(personExternalIdValue+",");
finalMessage.append(personPersonUUIDValue+",");
finalMessage.append(processingStatusValue+",");
Node personWorkAgreementExternalIdNode = ((Node) xPath.evaluate("./PersonWorkAgreementExternalID", workAgreementNode, XPathConstants.NODE));
String personWorkAgreementExternalIdValue = personWorkAgreementExternalIdNode.getTextContent();
finalMessage.append(personWorkAgreementExternalIdValue+",");
Node personWorkAgreementUUIDNode = ((Node) xPath.evaluate("./PersonWorkAgreementUUID", workAgreementNode, XPathConstants.NODE));
String personWorkAgreementUUIDValue = personWorkAgreementUUIDNode.getTextContent();
finalMessage.append(personWorkAgreementUUIDValue+"\n");
}
}
}
message.setBody(finalMessage.toString());
return message;
}
Step 5: Set file name
Using a Content Modifier set the target file name for the file.
Step 6: Send API result
On last step configure the inbound connection to your SFTP server. Use a different folder to store the API results.
Running the scenario
To start our integration scenario a file is added to the source folder of the SFTP server.
Wait for the polling time. The integration flow will start, read the file from the SFTP server and pass it to the CSV to XML converter.
After this step the payload will have an XML flat structure containing our Employee data.
<?xml version="1.0" encoding="UTF-8"?>
<ns0:root xmlns:ns0="http://www.example.org/basic_employee_import">
<employee>
<PersonUUID>
</PersonUUID>
<PersonExternalID>ET407</PersonExternalID>
<Username>
</Username>
<BusinessPartnerRole>BUP003</BusinessPartnerRole>
<Supplier>
</Supplier>
<FirstName>Harvey</FirstName>
<LastName>Specter</LastName>
<PersonFullName>Harvey Specter</PersonFullName>
<GenderCode>1</GenderCode>
<EmailAddress>
</EmailAddress>
<PhoneNumber>
</PhoneNumber>
<MobilePhoneNumber>
</MobilePhoneNumber>
<Language>EN</Language>
<CompanyCode>1010</CompanyCode>
<CostCenter>
</CostCenter>
<StartDate>20170401</StartDate>
<EndDate>99991231</EndDate>
<PersonWorkAgreementUUID>
</PersonWorkAgreementUUID>
<PersonWorkAgreementExternalID>ET407-1</PersonWorkAgreementExternalID>
</employee>
</ns0:root>
Next step executes a mapping from the XML flat structure to API XML structure. The result is the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<ns3:WorkforcePersonMasterDataReplicationRequest_sync xmlns:ns3="http://sap.com/xi/PASEIN">
<WorkforcePersonMasterData>
<PersonUUID>79fb0c90-cc78-4d2f-98f7-a03d1871d909</PersonUUID>
<PersonExternalID>ET407</PersonExternalID>
<UserName>
</UserName>
<PersonalInformation>
<FirstName>Harvey</FirstName>
<LastName>Specter</LastName>
<PersonFullName>Harvey Specter</PersonFullName>
<GenderCode>1</GenderCode>
</PersonalInformation>
<WorkAgreementInformation>
<PersonWorkAgreementUUID>4678d2bc-3dbc-45e4-a526-3ecee631191b</PersonWorkAgreementUUID>
<PersonWorkAgreementExternalID>ET407-1</PersonWorkAgreementExternalID>
<PersonWorkAgreementType>1</PersonWorkAgreementType>
<WorkAgreementJobInformation>
<ValidityPeriod>
<StartDate>2017-04-01</StartDate>
<EndDate>9999-12-31</EndDate>
</ValidityPeriod>
<WorkAgreementStatus>1</WorkAgreementStatus>
<CompanyCode>1010</CompanyCode>
<CostCenter>
</CostCenter>
</WorkAgreementJobInformation>
</WorkAgreementInformation>
</WorkforcePersonMasterData>
</ns3:WorkforcePersonMasterDataReplicationRequest_sync>
The request is sent to the S/4HANA Cloud API for Employee replication. The synchronous interface returns the response with the results.
<n0:WorkforcePersonMasterDataReplicationResponse_sync xmlns:n0="http://sap.com/xi/PASEIN" xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:prx="urn:sap.com:proxy:EAY:/1SAI/TAS47FED14EF0DCEA2C4738:772">
<WorkforcePersonMasterDataReplResponse>
<PersonUUID>79fb0c90-cc78-4d2f-98f7-a03d1871d909</PersonUUID>
<PersonExternalID>ET407</PersonExternalID>
<ProcessingStatusCode>01</ProcessingStatusCode>
<WorkforcePersonMasterDataReplConfLogMessage>
<PersonWorkAgreementUUID>4678d2bc-3dbc-45e4-a526-3ecee631191b</PersonWorkAgreementUUID>
<PersonWorkAgreementExternalID>ET407-1</PersonWorkAgreementExternalID>
<PersonWorkAgreement>50004703</PersonWorkAgreement>
<LogItem>
<TypeID>057(ECPAO_IN_PROCESSING)</TypeID>
<SeverityCode>I</SeverityCode>
<Note>Replication of employee data was successful for Employee ID ET407</Note>
</LogItem>
</WorkforcePersonMasterDataReplConfLogMessage>
</WorkforcePersonMasterDataReplResponse>
</n0:WorkforcePersonMasterDataReplicationResponse_sync>
Last step is to convert from the API XML service response to CSV and place it in the target destination folder.
That’s it! I hope you like it and if you have questions leave your comment bellow!
Great work! Thanks for this!
Good morning Carlos Dias
thank you for this 2 parts blog post.
I would like to know how could I get skills data from an Employee.
As far as I know (and understood), there is a skills library and a skill management system (maybe related to the Learning Management System). Do these data would be exposable by API ?
Thank you
Amaury
Hi Amaury,
Thank for your comment.
As of 1808 a new OData API is available to manage a person skill tags. You can find it on API HuB
Best regards,
/cd
Hi Carlos,
I am unable to test this service using SOAP UI. How to determine the endpoint of this SOAP inbound service. I am currently using the endpoint as https://host:port/sap/bc/srt/scs/sap/workforcepersonmasterdatarepli
I tried some options and getting Internal server error. Also I have set up a communication arrangement as mentioned. Please tell me the host name for communication system. How do we determine that.
Can you please help me
Carlos Dias - This blog of yours is very helpful but do you have any example for termination of employee using CPI ?
Thanks
Hi Carlos,
Thank you for the information on replicating the Employee data
I have one clarification: Can you please let me know how the PersonUUID and PersonalWorkAgreementUUID are generated.
When we are using SOAP API these are mandatory fields and are to be provided in the API. But we are not getting this alpha numeric number from the Third party system.
Can you let us know how this field value is to be handled. 1) is it to be generated in SCI or 2) read from S4HC(if so how) or 3) can be any arbitrary number?
thank you very much for your clarification
Thanks & Regards
Pavan M
Hi Carlos,
Thank you for this helpful blog.
Can I know how the alphanumeric PersonUUID and PersonWorkAgreementUUID fields were generated in the SOAP request profile (mapping output). Person UUID seems blank in the input CSV.
<PersonUUID>79fb0c90-cc78-4d2f-98f7-a03d1871d909</PersonUUID> and
<PersonWorkAgreementUUID>4678d2bc-3dbc-45e4-a526-3ecee631191b</PersonWorkAgreementUUID>
Thank You.