Skip to Content
Technical Articles
Author's profile photo Nikhil Walsetwar

SAP CPQ Integration with Salesforce CRM

Recently the SAP CPQ is recognized as  a Leader in the Gartner Magic quadrant. One of the best qualities I like about CPQ is, it’s a platform-agnostic quoting system and it can be easily integrated with many cross CRM solutions like, SAP C4C, SFDC CRM or even with the MS Dynamics CRM. You might have gone through my previous blog on C4C and CPQ integration how easily we can integrate these two systems for a seamless user experience.

In this blog post, we will understand how we can integrate CPQ with Salesforce CRM.

The integration is divided into two sections,

  1. Configuration in Salesforce CRM
  2. Configuration in SAP CPQ

1. Configuration in Salesforce CRM

  • Login to your SFDC instance with admin access. Navigate to Setup-> Object Manager -> New Object.
  • Fill in all required information, and then click on save.     
  • Navigate to Fields and Relationships -> Click on New field.
  • Select the relevant data type. And click on next. In this scenario, I’m creating a Quote ID field. The data type of this field is Number with length 18. -> Click on next.
  • Fill in the field label, name, description and help information if any and click on next.
  • Add the field level visibility and edit permission.
  • Follow the same steps and create the below fields,
Field name Data type Length
Discount Percent Percent Length – 16, Decimal Places – 2
Total List Price Currency Length – 16, Decimal Places – 2
Opportunity Lookup Relationship Related to Opportunity
Owner ID Number Length – 18, Decimal Places – 0
Primary Checkbox
Quote ID Number Length – 18, Decimal Places – 0
Total Net Price Currency Length – 16, Decimal Places – 2
Revision Number Text 10
  • Now, we need to create a visual force page to overwrite the standard UI behaviour. For this navigate to Setup->Build -> Custom Code -> Visual force page. Click on Create New View.
  • Create a new page for New Quote creation from SFDC to CPQ. Fill in all require details and copy-paste the below code in Visualforce Markup section. Here, please do not forget to select the checkbox ‘Available for Lightning Experience, Experience Builder sites, and the mobile app’.
  • Update the highlighted part of the code with your system details. Here, in this example I have updated the system URL and the domain name.
<apex:page standardController="Quote__c" showHeader="true" >
<script type="text/javascript">
    document.onreadystatechange = function () {
        var iframeElm = document.getElementById('iframe1');
        var parentStyle = window.getComputedStyle(iframeElm.offsetParent);
        var parentPaddingTop = parseInt(parentStyle.paddingTop);
        var parentPaddingBottom = parseInt(parentStyle.paddingBottom);
        var iframeBorder = parseInt(window.getComputedStyle(iframeElm).borderWidth) || 2;
        var iframeParentPadding = parentPaddingTop + parentPaddingBottom;
 
        // only do if on mobile 
        if((typeof sforce != 'undefined') && sforce && (!!sforce.one)){
            // when document is ready set height, width and style
            setHeight(iframeElm, iframeParentPadding, iframeBorder);
            setWidth(iframeElm, iframeBorder);
            setStyle(iframeElm); 
 
            // if window is resized adjust width again to be responsive
            window.addEventListener("resize", function () {
                setHeight(iframeElm, iframeParentPadding, iframeBorder);
                setWidth(iframeElm, iframeBorder);
            }, true);
        } else {
            setClasicHeight(iframeParentPadding, iframeBorder);
            window.addEventListener("resize", function () {
                setClasicHeight(iframeParentPadding, iframeBorder);
            }, true);
        }
    }
 
    function setClasicHeight(iframeParentPadding , iframeBorder) {
        var iframeEl = document.getElementById('iframe1');
        var headerEl = document.getElementById('AppBodyHeader');
        var footerEl = document.getElementsByClassName('bPageFooter')[0];
        var tableEl = document.getElementsByClassName('bodyDiv')[0];
 
        // Get height values for all Salesforce elements (header, footer and container padding)
        var sfElementsHeight = parseInt(headerEl.offsetHeight) + parseInt(footerEl.offsetHeight) + parseInt(window.getComputedStyle(tableEl).borderTopWidth);
 
        // Set minimum iframe height
        iframeEl.style.minHeight = '600px';
        iframeEl.style.height = window.getWindowHeight() - sfElementsHeight - iframeParentPadding - (2 * iframeBorder)  + "px";
    }
 
    function setHeight(iframeElm, iframeParentPadding, iframeBorder) {
        // height is set to full window
        var newHeight = window.innerHeight - iframeParentPadding - (2 * iframeBorder) + "px";
        iframeElm.style.height = newHeight;
        // if not already set set maxHeight to iframe url so that SAP CPQ sets correct size
        if (iframeElm.src.indexOf("maxHeight") === -1) {
            iframeElm.src = iframeElm.src + "&maxHeight=" + newHeight;
        }
    }
 
    function setWidth(iframeElm, iframeBorder) {         
        // Set width to parent width - 2 * border size to avoid horizontal scroll
        var widthValue = document.body.clientWidth - (2 * iframeBorder) + "px";
        iframeElm.style.width = widthValue;
    }
 
    function setStyle(iframeElm){        
        //Get style of parent element
        var parentStyle = window.getComputedStyle(iframeElm.offsetParent);
        var parentLeft = parentStyle.paddingLeft;
        var parentRight = parentStyle.paddingRight;
 
        // setting margin and overflow
        iframeElm.style.marginLeft = '-' + parentLeft;
        iframeElm.style.marginRight = '-' + parentRight;
        iframeElm.style.overflow = "hidden";
    }
</script>
<iframe id="iframe1" width="100%" scrolling="true" src="https://sandbox.webcomcpq.com/salesforce/SfLogin.aspx?sfauthUserID={!$User.Username}&apiPartnerURL={!$Api.Partner_Server_URL_290}&apiSessionID={!$Api.Session_ID}&domain_name=TestDomain&apiPass=Password&action=New&sfqpOpportunityID={!Quote__c.Opportunity__c}" />
</apex:page>
  • Create one more page for edit quote scenario. Update the existing code with below attached code and the system URL details.
   <apex:page standardController="Quote__c" showHeader="true" >
<script type="text/javascript">
    document.onreadystatechange = function () {
        var iframeElm = document.getElementById('iframe1');
        var parentStyle = window.getComputedStyle(iframeElm.offsetParent);
        var parentPaddingTop = parseInt(parentStyle.paddingTop);
        var parentPaddingBottom = parseInt(parentStyle.paddingBottom);
        var iframeBorder = parseInt(window.getComputedStyle(iframeElm).borderWidth) || 2;
        var iframeParentPadding = parentPaddingTop + parentPaddingBottom;
 
        // only do if on mobile 
        if((typeof sforce != 'undefined') && sforce && (!!sforce.one)){
            // when document is ready set height, width and style
            setHeight(iframeElm, iframeParentPadding, iframeBorder);
            setWidth(iframeElm, iframeBorder);
            setStyle(iframeElm); 
 
            // if window is resized adjust width again to be responsive
            window.addEventListener("resize", function () {
                setHeight(iframeElm, iframeParentPadding, iframeBorder);
                setWidth(iframeElm, iframeBorder);
            }, true);
        } else {
            setClasicHeight(iframeParentPadding, iframeBorder);
            window.addEventListener("resize", function () {
                setClasicHeight(iframeParentPadding, iframeBorder);
            }, true);
        }
    }
 
    function setClasicHeight(iframeParentPadding , iframeBorder) {
        var iframeEl = document.getElementById('iframe1');
        var headerEl = document.getElementById('AppBodyHeader');
        var footerEl = document.getElementsByClassName('bPageFooter')[0];
        var tableEl = document.getElementsByClassName('bodyDiv')[0];
 
        // Get height values for all Salesforce elements (header, footer and container padding)
        var sfElementsHeight = parseInt(headerEl.offsetHeight) + parseInt(footerEl.offsetHeight) + parseInt(window.getComputedStyle(tableEl).borderTopWidth);
 
        // Set minimum iframe height
        iframeEl.style.minHeight = '600px';
        iframeEl.style.height = window.getWindowHeight() - sfElementsHeight - iframeParentPadding - (2 * iframeBorder)  + "px";
    }
 
    function setHeight(iframeElm, iframeParentPadding, iframeBorder) {
        // height is set to full window
        var newHeight = window.innerHeight - iframeParentPadding - (2 * iframeBorder) + "px";
        iframeElm.style.height = newHeight;
        // if not already set set maxHeight to iframe url so that SAP CPQ sets correct size
        if (iframeElm.src.indexOf("maxHeight") === -1) {
            iframeElm.src = iframeElm.src + "&maxHeight=" + newHeight;
        }
    }
 
    function setWidth(iframeElm, iframeBorder) {         
        // Set width to parent width - 2 * border size to avoid horizontal scroll
        var widthValue = document.body.clientWidth - (2 * iframeBorder) + "px";
        iframeElm.style.width = widthValue;
    }
 
    function setStyle(iframeElm){        
        //Get style of parent element
        var parentStyle = window.getComputedStyle(iframeElm.offsetParent);
        var parentLeft = parentStyle.paddingLeft;
        var parentRight = parentStyle.paddingRight;
 
        // setting margin and overflow
        iframeElm.style.marginLeft = '-' + parentLeft;
        iframeElm.style.marginRight = '-' + parentRight;
        iframeElm.style.overflow = "hidden";
    }
</script>
<iframe id="iframe1" width="100%" scrolling="true" src="https://sandbox.webcomcpq.com/salesforce/SfLogin.aspx?sfauthUserID={!$User.Username}&apiPartnerURL={!$Api.Partner_Server_URL_290}&apiSessionID={!$Api.Session_ID}&domain_name=TestDomain&apiPass=Password&action=Edit&sfqpOpportunityID={!Quote__c.Opportunity__c}&OwnerId={!CEILING(Quote__c.Owner_Id__c)}&QuoteId={!CEILING(Quote__c.Quote_Id__c)}"/>
 
</apex:page>
  • Now, we need to modify the standard behavior of the above created custom object. So that it will load the CPQ quote whenever we create a new instance of the custom object. For this navigate back to the custom object builder and select the related custom object.
  • Navigate to Button, Links and Actions menu -> Select the ‘New’ button and edit the behavior of the new button action. Make the configuration change as highlighted,
  • Repeat the same action for edit action,
  • Now we need to link this custom quote object to the standard SFDC opportunity object. For this navigate to Setup -> Object Manager and Search for Opportunity.
  • Open it and navigate to page layouts. Edit the layout ‘Opportunity (Sales) Layout’. Select the ‘Related List’ section and drag and drop the newly created ‘Sales Quotes’ list.
  • Save the layout.

2.Configuration in SAP CPQ

  • Login to the CPQ system via admin user.
  • Navigate to the CRM Integration -> General. Select the Salesforce from the drop-down list. Fill all mandatory parameters based on the scenarios to be covered and click on save.
  • Navigate to CRM Integration -> CRM Administrator Account, to maintain the SFDC admin user details. Here the password is a combination of the login password and the security token. For example my password is ‘Password123’ and the security token is ‘aaaaaaaaaa’ then in CPQ I need to maintain the password as ‘Password123 aaaaaaaaaa’.
  • The security token can be copied from Salesforce under User->Settings -> My Security token.
  • We need to map the CRM custom object with CPQ Quote. This is one of the important steps for this integration. This will update the sales quote custom object details in the CRM from CPQ. To achieve this navigate to the CRM Integration -> CRM Quote. Here the CRM Quote Object Name is the technical name of the custom business object we have created in the step 2 under configurations in CRM section. Map the quote header fields using the ctx tag as shown in the below screenshot.
  • Now, we need to map the users between these two systems. The CRM user and CPQ user will automatically mapped when the user logins to the CPQ for the first time inside the SFDC iframe based quote integration. From that point the user will not be prompted for the credentials, the access and authentication will automatically happen. As an administrator if you would like to skip the credential checks for the first time then map the CRM and CPQ users as highlighted,
  • Here, we must map the pricebook and market within Salesforce CRM and the SAP CPQ. For this navigate to the CRM Integration -> Price Book Market Mappings.Here in this scenario the standard CRM pricebook is maped with one of the markets in CPQ. You can also use the CRM Lookup to read all existing pricebooks in the Salesforce CRM. Save the mapping.
  • Now, here comes one of the important mapping between these two systems. And it is the customer mapping. In CPQ we can use 3 customer types as, Bill-to, Ship-to and End Customer. For the current example let’s map Bill-to and Ship-to for Quote creation/updation scenario.
  • After the role level mapping we need to complete the field level mapping with these two roles. You can use the CRM Lookup to read CRM fields, this will also show the custom fields if any created at Salesforce CRM side. Save the mapping.
  • Repeat the same steps for Ship to as well.
  • Now, we need to map the opportunity and quote statuses. For this navigate to CRM Integrations-> Opportunity Statuses. Add a new mapping, in this scenario I have consider an example as the Customer rejected quote status is map with the Closed Lost.

 

Now to test the integration create an opportunity in the Salesforce CRM, navigate to Sales Quotes section -> Create a new quote. System will automatically route you to the SAP CPQ and it’ll create a new quote. Happy integrating. 😊

 

Regards,

Nikhil

 

Assigned tags

      Be the first to leave a comment
      You must be Logged on to comment or reply to a post.