Friday, 30 October 2015

Using Groovy Language in Dell Boomi integration Process to get the IDOC nodes



Salesforce
This topic is related to Boomi integration, the scenario I am now covering is to concatenate the specific XML tags. For instance I have a below IDOC document tags

<?xml version="1.0" encoding="UTF-8"?>
<E1EDKT1>
   <TDID>Z007</TDID>
   <TSSPRAS>E</TSSPRAS>
   <TSSPRAS_ISO>EN</TSSPRAS_ISO>
   <E1EDKT2>
      <TDLINE>SalesOrderNotesLine1</TDLINE>
      <TDFORMAT>*</TDFORMAT>
   </E1EDKT2>
   <E1EDKT2>
      <TDLINE>SalesOrderNotesLine2</TDLINE>
      <TDFORMAT>*</TDFORMAT>
   </E1EDKT2>
   <E1EDKT2>
      <TDLINE>SalesOrderNotesLine3</TDLINE>
      <TDFORMAT>*</TDFORMAT>
   </E1EDKT2>
   <E1EDKT2>
      <TDLINE>SalesOrderNotesLine4</TDLINE>
      <TDFORMAT>*</TDFORMAT>
   </E1EDKT2>
   <E1EDKT2>
      <TDLINE>SalesOrderNotesLine5</TDLINE>
      <TDFORMAT>*</TDFORMAT>
   </E1EDKT2>
   <E1EDKT2>
      <TDLINE>SalesOrderNotesLine6</TDLINE>
      <TDFORMAT>*</TDFORMAT>
   </E1EDKT2>
   <E1EDKT2>
      <TDLINE>SalesOrderNotesLine7</TDLINE>
      <TDFORMAT>*</TDFORMAT>
   </E1EDKT2>
   <E1EDKT2>
      <TDLINE>SalesOrderNotesLine8</TDLINE>
      <TDFORMAT>*</TDFORMAT>
   </E1EDKT2>
   <E1EDKT2>
      <TDLINE>SalesOrderNotesLine9</TDLINE>
      <TDFORMAT>*</TDFORMAT>
   </E1EDKT2>
   <E1EDKT2>
      <TDLINE>SalesOrderNotesLine10</TDLINE>
      <TDFORMAT>*</TDFORMAT>
   </E1EDKT2>
   <E1EDKT2>
      <TDLINE>SalesOrderNotesLine11</TDLINE>
      <TDFORMAT>*</TDFORMAT>
   </E1EDKT2>
   <E1EDKT2>
      <TDLINE>SalesOrderNotesLine12</TDLINE>
      <TDFORMAT>*</TDFORMAT>
   </E1EDKT2>
   <E1EDKT2>
      <TDLINE>SalesOrderNotesLine13</TDLINE>
      <TDFORMAT>*</TDFORMAT>
   </E1EDKT2>
</E1EDKT1>

In above IDOC entries, I need to fetch first 10 TDLINE tag and make it to one single string and after first 10, it would discard all the TDLINE. So for this, we need to write the Groovy scripting in Dell Boomi integration process. Please see below process image.



In above figure first it gets the SAP Order IDOC and just before map I used Data Process node, I have written the groovy scripting language which would concatenate the first 10 lines of above order IDOC and and set the result in Property and after that in map it would use that Property to map with Salesforce object. See the below groovy code.
import java.util.Properties;
import java.io.InputStream;
import org.jdom.input.SAXBuilder;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.xpath.XPath;
import org.jdom.output.XMLOutputter;
import com.boomi.execution.ExecutionUtil;

for( int i = 0; i < dataContext.getDataCount(); i++ ) {

 InputStream is = dataContext.getStream(i);
 Properties props = dataContext.getProperties(i);
  
 // Build XML Document
 SAXBuilder builder = new SAXBuilder();
 Document doc = builder.build(is);

 XPath PricingNotes;
 PricingNotes = XPath.newInstance("//IDOC/E1EDKT1[TDID='Z007']/E1EDKT2/TDLINE");

 XPath SalesNotes;
 SalesNotes = XPath.newInstance("//IDOC/E1EDKT1[TDID='Z002']/E1EDKT2/TDLINE");

 
 MSGFN = PricingNotes.selectNodes(doc);
 PARWN = SalesNotes.selectNodes(doc);

 Integer NotesCount = 0;
 Integer NotesCount2 = 0;
 String TdLine = "";
 String TdLine2 = "";
 result = new StringBuffer();
 result2 = new StringBuffer();

                for(int ak=0;ak<MSGFN.size();ak++){
            if(NotesCount < 10){
                                result.append(MSGFN[ak].getText()+System.getProperty('line.separator'));
                NotesCount = NotesCount +1;
            }
                }
               
                for(int ab=0;ab<PARWN.size();ab++){
           if(NotesCount2 < 10){
                result2.append(PARWN[ab].getText()+System.getProperty('line.separator'));
                NotesCount2 = NotesCount2 +1;
           }
                }
               
                                TdLine = result.toString();
                TdLine2 = result2.toString(); 
 // Create an XPath statement to search for the element or elements you care about:

                  ExecutionUtil.setDynamicProcessProperty("SalesOrderNotes", TdLine , true);
                  ExecutionUtil.setDynamicProcessProperty("PricingNotes", TdLine2 , true);


     XMLOutputter outputter = new XMLOutputter();
     is = new ByteArrayInputStream(outputter.outputString(doc).getBytes("UTF-8"));

     dataContext.storeStream(is, props);
}

If you see the above yellow highlighted code, it is fetching  only those nodes which are under E1EDKT1 main tag and surely this tag exists multiple time in IDOC, so to capture only those E1EDKT1 node which has a qualifier of [TDID='Z007'] and after that it would to the child node E1EDKT2 and in that child node the TDLINE tag exists. So we used XPATH to fetch the XML node and similar logic used in second yellow highlighted line but this time Qualifier is change. After that the green highlighted code shows that it select the specific XPATH criteria node. After that, gray highlighted code set the TDLINE text into the groovy PROPERTY which are “SalesOrderNotes” and “PricingNotes”.



Now it’s about to turn for MAP node. In Map, you would use the Get Dynamic Process property and get the property that we have set in groovy code.


 You can than map this property to any of the Salesforce object field.

Monday, 26 October 2015

Calling Boomi process from Salesforce page and getting the data from SAP environment using HTTP Request


Salesforce
The topic which i am covering here to show the PDF on apex page, this is very simple but the main thing over here is that i need to fetch the data from SAP system through Boomi and than after it takes the data from Boomi in Salesforce than it would show as PDF. So first i am showing my apex page code here. The reason being I am using dynamic rendering is that if there would be no data to show than it should show the blank white page with appropriate message, if I would not use the condition than if it would not contain any data than it is showing blank BLACK pdf style without data.
<apex:page controller="GenerateInvoicePdf" sidebar="false">
     <apex:pageMessages />
<script>
        var isPDF = '{!bJSEnable}';
        if (isPDF=='true')
        {
           window.location.href = "data:application/pdf;base64,{!sData}";
        }
    </script>
</apex:page>
I am showing you the controller of this page, in this controller, I using HTTP call to pass the request envelop from Salesforce to Boomi (Integration tool) and get the response envelope from Boomi to saleforce. This below function returns HTTPResponse. First it is getting Endpoint from custom lable.
public static HttpResponse getInfoBoomi()
    {
        // Get the Endpoint URL from custom label     
        String EndPointUrl = Label.CGI_Invoice_Pdf_Boomi_Endpoint_Url; 
        Httprequest request=new Httprequest();
        request.setmethod('POST');
        request.setTimeout(120000);
        request.setEndpoint(EndPointUrl);    
        //set the request string for Boomi service connector containing Invoice No which will than pass to SAP to get the Invoice Base64 code
        string strInput='<?xml version="1.0" encoding="UTF-8"?>' +
                        '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' +
                        '   <soapenv:Body> <setOutput xmlns="https://secure.logmeinrescue.com/API/API.asmx"> <eOutput>XML</eOutput></setOutput>' +
                        '      <notifications xmlns="http://soap.sforce.com/2005/09/outbound">' +
                        '         <Notification> ' +
                        '               <InvoceId>'+ ApexPages.currentPage().getParameters().get(CGIConstant.Invoice_PDF_Query_String) +'</InvoceId>' +       
                        '         </Notification>' +
                        '      </notifications>' +
                        '   </soapenv:Body>' +
                        '</soapenv:Envelope>';
                                   
         //replace all & with &amp; to avoid xml encoding issue                     
                                 
         strInput=strInput.replaceAll('&','&amp;');
         request.setBody(strInput);
         Http httprequest=new Http();
         string responseMessage;      
         HttpResponse res;
         //send http request to boomi process
         res=httprequest.send(request);

         return res;
     }

The value of Label.CGI_Invoice_Pdf_Boomi_Endpoint_Url = https://test.connect.boomi.com/ ws/simple/getInvoice; boomi_auth=dGhlY2hhbWJlcmxhaW5ncm91cGluYy1US1pBRDY=. If you noticed that this contains service URL coming from Boomi Web service server operation URL path, see the below image red block, the operation also contains Request profile as well as response profile. Request profile contains the HTTP request envelope coming from Salesforce which is yellow highlighted area in above table. This request receive by Boomi through the below operation. Now the after Simple URL path in above Endpoint URL we need to put “;boomi_auth=” and after that it needs the Account Id and Token concatenate by Colon (:) and then user should convert the whole string into base64 like in our case the Account Id is “TestAccount” and Token is “HGFTH76-JGBVFF876-KJKBV23”. This account info can be get from Boomi Atom Management under Shared Web Server settings. So after getting this information, we need to concatenate both the things and put colon in between them like  TestAccount: HGFTH76-JGBVFF876-KJKBV23 than convert this string into the base 64 and then put that encoded value after boomi_auth in above URL. If you wee in above source code it is passing Invoice Id from apex controller to Boomi using Http request envelop and that Invoice Id is fetching from query string.
FIGURE 1


Now I am going to cover the Boomi Process. If you see below image which is showing whole Process which is actually covering the scenario.
FIGURE 2


On first step, I am using the Web Service Server connector which receive data from Salesforce and as you already seen its operation in figure 1. This operation has request and response part. Request XML contains the same XML meta data schema as Request envelope which I covered in first table containing yellow highlighted area. Response would be contains those XML tags which your SAP system would return. So you can make your response after seeing the response from SAP. In above figure 2, in map part I am mapping the request Invoice Id to SAP request profile, see the below image

FIGURE 3 


For SAP, I have used the BAPI, and consuming the BAPI which contains Request and Response profile.
FIGURE 4 


The important thing here is that how to return the response envelop from Boomi to Salesforce so I have used the Return Document, which actually returns the response to that source which actually send the request. I also wanted to cover the message properties. I am using the service response envelope message into the message node in which I am passing the whole encoded base 64 Invoice PDF from SAP in between the Ack tag please see the below image.



Actually I have a Generate Invoice button in Invoice detail page which open this above apex page as a pop passing Invoice Number and this Invoice Number than passed to the SAP through Boomi. So when user click this button and get the proper response than we get the status code 200 which is Http success response. There are few more generic responses which might user get, so developer should maintain the proper error handling for end user. I am pasting the whole controller code here which will guide you through the end.
public with sharing class GenerateInvoicePdf{
    transient Blob bString;
    public string sData  {get; set;}
    public Boolean bJSEnable  {get; set;}
    public static HttpResponse getInfoBoomi()
    {
        // Get the Endpoint URL from custom label     
        String EndPointUrl = Label.CGI_Invoice_Pdf_Boomi_Endpoint_Url; 
        Httprequest request=new Httprequest();
        request.setmethod('POST');
        request.setTimeout(120000);
      
        request.setEndpoint(EndPointUrl);    
        //set the request string for Boomi service connector containing Invoice No which will than pass to SAP to get the Invoice Base64 code
        string strInput='<?xml version="1.0" encoding="UTF-8"?>' +
                        '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' +
                        '   <soapenv:Body> <setOutput xmlns="https://secure.logmeinrescue.com/API/API.asmx"> <eOutput>XML</eOutput></setOutput>' +
                        '      <notifications xmlns="http://soap.sforce.com/2005/09/outbound">' +
                        '         <Notification> ' +
                        '               <InvoceId>'+ ApexPages.currentPage().getParameters().get(CGIConstant.Invoice_PDF_Query_String) +'</InvoceId>' +       
                        '         </Notification>' +
                        '      </notifications>' +
                        '   </soapenv:Body>' +
                        '</soapenv:Envelope>';
                                   
         //replace all & with &amp; to avoid xml encoding issue                     
                                 
         strInput=strInput.replaceAll('&','&amp;');
         request.setBody(strInput);
         Http httprequest=new Http();
         string responseMessage;      
         HttpResponse res;
         //send http request to boomi process
         res=httprequest.send(request);

         return res;
        
    }
    public void Initializedata()
     {
         HttpResponse res = getInfoBoomi();
         sData = res.getbody();
         Boolean result = String.isEmpty(sData);
         string extraNodes = '';
         if (res.getStatusCode() == integer.valueof(Label.HTTP_SUCCESS_REQUEST))    
         {
             if (sData.contains(CGIConstant.HTTP_Boomi_Response_Ack_Having_Null)|| sData == '')
             {
                 bJSEnable = false;
                 ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.WARNING ,Label.ERR_INV_DOESNOT_EXISTS));
             }
             else
             {
                 if ((sData.IndexOf(CGIConstant.HTTP_Boomi_Response_Ack_Start_Tag) < 0) && (sData.IndexOf(CGIConstant.HTTP_Boomi_Response_Ack_End_Tag) < 0))
                 {
                    bJSEnable = false;
                    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.WARNING ,Label.ERR_INV_DOESNOT_EXISTS));
                 }
                 sData = sData.substring(sData.IndexOf(CGIConstant.HTTP_Boomi_Response_Ack_Start_Tag) + 5);
                 extraNodes = sData.Substring(sData.IndexOf(CGIConstant.HTTP_Boomi_Response_Ack_End_Tag));
                 sData =sData.replace(extraNodes ,'');
                
             }
            
         }
         else if(res.getStatusCode() == integer.valueof(Label.ERR_HTTP_BAD_REQUEST))
             ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.WARNING , Label.ERR_HTTP_BAD_REQUEST_MESSAGE));
         else if(res.getStatusCode() == integer.valueof(Label.ERR_HTTP_UNAUTHORIZE_REQUEST))
             ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.WARNING , Label.ERR_HTTP_UNAUTHORIZE_REQUEST_MESSAGE));
         else if(res.getStatusCode() == integer.valueof(Label.ERR_HTTP_NOT_FOUND_REQUEST))
             ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.WARNING , Label.ERR_HTTP_NOT_FOUND_REQUEST_MESSAGE));
         else if(res.getStatusCode() == integer.valueof(Label.ERR_HTTP_INTERNAL_REQUEST))
             ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.WARNING , Label.ERR_HTTP_INTERNAL_REQUEST_MESSAGE));
         else if(res.getStatusCode() == integer.valueof(Label.ERR_HTTP_UNAVAILABLE_REQUEST))
             ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.WARNING , Label.ERR_HTTP_UNAVAILABLE_REQUEST_MESSAGE));
    }
 
    public GenerateInvoicePdf()
    {
       bJSEnable = true;
       Initializedata();
    }
   
 }
In above code if you see, I have covered all the Http request and show them as proper message for the user.