Conga Product Documentation

Welcome to the new doc site. Some of your old bookmarks will no longer work. Please use the search bar to find your desired topic.

Show Page Sections

Custom Integration

In the following chapter we describe how to customize the standard CPQ integration

Warning: These customizations require you to:

Understand the way PROS Quote is integrated into Salesforce

Know how to code using APEX language

Note: The standard PROS Quote behavior must be de-activated. The PROS Custom setting Enable Triggermust be set to ‘false’

Understanding the PROS Quote Integration

The following sections describe the integration components details of CPQ for Force.com.

UNDERSTANDING THE INTEGRATION SEQUENCE

The steps to invoke CPQ from within Salesforce and the opposite have been described in the paragraphs concerning the architecture. The following schema below shows the usage sequence of integration components:



How to Customize the CPQ Call

The call to CPQ is performed by an APEX class called from the VisualForce page controller. As it is a component of CPQ managed package, this class cannot be modified nor overridden.

You can however:

  • add extra fields to the PROS Quote object > The values will be automatically sent to CPQ
  • or parameterize the Mapping Set > The selected values from the quote linked entities will be automatically sent to CPQ

It is also possible to send to CPQ values that are out of the scope of the standard integration, which may not be related to the Quote object or its linked entities.

These values will be retrieved or computed by a custom Apex class that will be developped and deployed locally on customer’s Force.com organization.

The custom class will be invoked in the process of calling CPQ, when the user clicks on the Content tab of the Quote detail page.

Below is an example of a local Apex class that sends out back to the CPQ calling process a list of values that will be initialized in CPQ context in the form of attributes. Attributes can be used to populate quote fields or total cells as well as in Configurator and reports models.

global with sharing class MyMappingIN

implements CameleonCPQ.CPQRequestFormatter.IMappingIN

{

/* Returns a list of additional attributes to be sent to CPQ */

global List<CameleonCPQ.CPQRequestFormatter.Tuple> getCPQCustomSettings(Id entityId)

{

List<CameleonCPQ.CPQRequestFormatter.Tuple> sessionXmlElts =

new List< CameleonCPQ.CPQRequestFormatter.Tuple>();

sessionXmlElts.add(new CameleonCPQ.CPQRequestFormatter.Tuple('MY_CUSTOM_SETTING','This setting has been set

by a custom APEX class'));

return sessionXmlElts;

}

}

The local class must implement the interface

CameleonCPQ.CPQRequestFormatter.IMappingIN

And therefore provide an implementation for all of its methods

The following table lists the methods of the interface

CameleonCPQ.CPQRequestFormatter.IMappingIN.

Methods of interface CameleonCPQ.CPQRequestFormatter.IMappingIN

METHOD RETURNED TYPE DESCRIPTION
getCPQCustomSettings(Id QuoteId) List<CameleonCPQ.CPQRequestFormatter.Tuple> The Id in input of the method is giving the Force.com Id of the current Quote object. The output is a list of Tuple elements.
A Tuple element is a code / value pair.

When the local class has been deployed in the Force.com organization, activate it by setting its name (without any extension) in the PROS custom settings:



How to Override the Standard Synchronization Mechanism or Add a New Trigger

Within the “Content” tab of a PROS Quote, a button launches the synchronization process from CPQ to the PROS Quote object. This synchronization is done by calling the APEX

class CPQSyncService deployed as a Web Service.

The CPQSyncService APEX class creates a Quote Content record related to the PROS Quote and attaches to it the XML content of the Quote received from CPQ. An “Attachment” record is created.

The event “Update Quote Content” fires the trigger CPQSyncService, which updates the PROS Quote fields based on the principle described in section 6.2.1.2

This standard synchronization mechanism can be discarded and overridden by a custom procedure: In that case, uncheck the option “Enable Standard Trigger” in the PROS custom settings (see section 5.2).

You can also enhance the default synchronization procedure by writing additional Apex code.

In both cases, the customization of the synchronization process requires the creation and writing of a new trigger.

To add your custom trigger:

  • Click on Create in “App Setup”
  • Click on Objects and select the PROS Quote Content Object.
  • In the Triggers section you can add a new trigger by clicking on New button.

As mentioned before, the quote content of a PROS Quote is not available by executing SOQL requests against CPQ objects: It is stored in XML format, in an attachment related to the PROS Quote Content object.

In order to retrieve fields’ values from the quote content, you need first to retrieve the XML representation of the quote from the related attachment and then, parse it.

The example below illustrates the creation, in Salesforce, of products chosen in CPQ catalogs, and added to the quote content.

On CPQ side, the code and description identifiers of a line item are given by the quote model.

When the end-user clicks on the Synchronize and Back to Overview, the custom trigger added to the Quote Content object is fired:

trigger MyCustomSynchronization on CameleonCPQ QuoteContent c (after update) {

for (CameleonCPQ QuoteContent c qc : [SELECT CameleonCPQ QuoteId c FROM CameleonCPQ QuoteContent c WHERE Id IN: Trigger.newMap.keySet()])

{

/* Get PROS quote content XML from Attachment (note '_content' suffix)

*/

Attachment[] att = [SELECT Id, Body FROM Attachment WHERE parentId=:qc.Id AND Name LIKE '%_content' LIMIT 1];

Blob quoContentXML = att[0].Body;

/* Invoke the Parser utility */

CameleonCPQ.CPQParser quoteParser = new CameleonCPQ.CPQParser(quoContentXML);

CameleonCPQ.CPQParser.Quote quote = quoteParser.getQuote();

/* Get the lines of the quote */

List<CameleonCPQ.CPQParser.QuoteLine> qlines = quote.getAllLines();

/* Gets the fields of a quote line */

for (CameleonCPQ.CPQParser.QuoteLine qline : qlines)

{

Map<String,String> columns = qline.getColumns();

/* Get the values of columns ‘ItemID’ and ‘ItemDescr’ */

String prCode = columns.get('ItemID');

String prDescr = columns.get('ItemDescr');

Product2 pr = new Product2(ProductCode= prCode, Name= prCode, Description= prDescr, IsActive= true);

upsert pr;

}

}

}

cr'); Product2 pr = new Product2(ProductCode= prCode, Name= prCode, Description= prDescr, IsActive= true); upsert pr; } } }

As you can see in the example, you don’t need to have the full knowledge of the XML structure of the quote content to retrieve the fields you want.

The APEX class CPQParser is provided to help you parsing the xml content of a PROS Quote thanks to a set of inner objects and methods.

Tip: Keep in mind the structure of a CPQ Quote, which is globally divided in 3 parts:
  • The header fields, subdivided in sections, are related to the Quote
  • The lines fields (columns) are related to a line. The lines are related to the Quote.
  • The total cells which somehow aggregate the lines values are related to the Quote.

The APEX class CPQParser publishes methods to retrieve 3 types of objects (inner classes of CPQParser):

  • CPQQuote: The Quote
  • CPQQuoteLine: A Quote Line
  • CPQDocument: A document generated during the CPQ session

The parsing of quote content XML is performed by:

CameleonCPQ.CPQParser quoteParser = new CameleonCPQ.CPQParser(quoteContent);

(quoteContent is of type Blob)

The following table list the methods that you could use to retrieve data from Quote Content:

Methods of CameleonCPQ.CPQParser

METHODRETURNED TYPERETURNED VALUE DESCRIPTION
getQuote()CameleonCPQ.CPQParser.QuoteThe complete representation of a quote

Methods of CameleonCPQ.CPQParser.Quote

METHODRETURNED TYPEDESCRIPTION
getId()CameleonCPQ.CPQParser.QuoteLineThe ID of the quote
getDomain()StringThe domain of the quote
getRelease()StringThe release number of the current quote content
getFields()Map<String,String>The list of header fields and total cells along with their values. Header fields names are provided with the name of their section as a prefix: ParameterTab.fieldName AddressTab.fieldName CartInfoTab.fieldName OrderInfoTab.fieldName ChangeAndControlTab.fieldName
getFieldsWithoutTab()Map<String,String>The list of header fields and total cells along with their values. Section name is not present in the field name.
getField(String fieldName)StringReturn the value of a field. For a field header, the name of section is required.
getLines(String type)CameleonCPQ.CPQParser.QuoteLine[]Returns the list of lines and their sub-lines which type is matching the given type. Types are: CP7 : Configured line item SI7 : Standard item part of a configured item breakdown CT7 : Catalog item FO : Folder SP : Specific item
getAllLines()CameleonCPQ.CPQParser.QuoteLine[]Returns the list of lines and their sub-lines

Methods of CameleonCPQ.CPQParser.QuoteLine

METHODRETURNED TYPEDESCRIPTION
getId()StringThe internal line number
getLineType()StringThe type of line - Line Types are: CP7 : Configured line item SI7 : Standard item part of a configured item breakdown CT7 : Catalog item FO : Folder SP : Specific item
getSeqNum()StringThe internal display sequence number
getIsSubline()BooleanA flag that indicates if the line is a sub-line or not. Sub-lines can be lines under a folder or can be part of the breakdown related to a configured item.
hasSublines()BooleanA flag that indicates if the line has sub-lines
getLevel()IntegerThe level of the line. Top level lines are at level 0. Sub-lines have a level number > 1
getSublines()CameleonCPQ.CPQParser.QuoteLine[]Returns the list of sub-lines of the current line, but only those at lower level.
getAllLines()CameleonCPQ.CPQParser.QuoteLine[]Returns the list of sub-lines of the current line, for all levels.
getColumns()Map<String,String>The list of columns along with their values for the current line. To retrieve the value of a particular column, invoke the get(columnName) method on the returned map.

Methods of CameleonCPQ.CPQParser.QuoteDocument

METHODRETURNED TYPERETURNED VALUE DESCRIPTION
No method availableNo method availableNo method available

How to Call a Stateless Action

Stateless actions can be used to make calls to CPQ services from Force.com. These actions can be invoked at any step of the quotation process.

This configuration implies to:

  • Use the stateless method provided in the Force.com package with the corresponding Quote model action:

    /****************************************

    * entityId: Id of PROS Quote object

    * release: PROS Quote Active Release

    * operationType : <openReleaseStateless> (fixed value)

    * actionName: Name of CPQ Quote Model action

    * soapUrl: Salesforce.com Web Service endpoint URL (if callback required)

    *****************************************/

    final String[] CPQResult = CameleonCPQ.CPQRequestFormatter.executeCPQAction(Id entityId, String release, String operationType, String actionName, String soapUrl)

  • Configure the corresponding stateless action in the CPQ Quote Designer:

For instance, a “stateless print” action can be configured to generate proposal documents directly from the PROS Quote in Force.com, without requiring to open CPQ.

In that example, the two steps above can be implemented as follow:

  1. Implement the call to the stateless method in an Apex class in SFDC:

    ApexClass[] apx = [SELECT ApiVersion FROM ApexClass c where c.Name like '%CPQRequestFormatter'];

    final String ApiVersion = (apx.size()>0?apx[0].ApiVersion.format():'30.0');

    final String wsURL =

    URL.getSalesforceBaseUrl().toExternalForm()+'/services/Soap/c/'+ApiVersion+'/';

    final String[] CPQResult = CameleonCPQ.CPQRequestFormatter.executeCPQAction('a01A000000IKyfM','1','openRele aseStateless','PrintStateless',wsURL);

  2. Add an Automated Action, triggered by a ‘StatelessExecutionMode’ (i.e. the action can be launched when CPQ Web service is launched in silent mode) and of type JavaClass pointing to the following class:

com.sfdc.integ.output.ExecutePrintStatelessAction





How to Override the CPQ Custom Settings

Within the PROS Custom Settings for the CPQ managed package in SFDC, it’s possible to link the CPQ Quote model with SFDC.

Note: See the chapter Link the CPQ Quote model to learn more about CPQ custom settings.

These settings can be overridden to customize their behavior and adapt them to your needs. A custom Apex class has to be developed and deployed locally on customer’s Force.com organization. This method will be called in order to override values defined in the custom settings.

Below is an example of a local Apex class to override these settings:

global with sharing class MyMappingIN_Init_Back_Act implements CameleonCPQ.CPQRequestFormatter.IMappingIN_Ext1 {

// Returns a list of additional custom settings to be sent to CPQ (entityId is the Id of current Quote)

global List<CameleonCPQ.CPQRequestFormatter.Tuple> getCPQCustomSettings(Id entityId) {

List<CameleonCPQ.CPQRequestFormatter.Tuple> sessionXmlElts = new List< CameleonCPQ.CPQRequestFormatter.Tuple>();

sessionXmlElts.add(new CameleonCPQ.CPQRequestFormatter.Tuple('MY_CUSTOM_SETTING','This setting has been set by a custom APEX class'));

// Return null if you do not want to add extra custom settings

return sessionXmlElts;

}

// Overrides the CPQ custom settings

global CameleonCPQ.CPQRequestFormatter.CPQSessionSettings getCPQSessionSettings(Id entityId, CameleonCPQ.CPQRequestFormatter.CPQSessionSettings sessionSettings) {

CameleonCPQ.CPQRequestFormatter.CPQSessionSettings

customSessionSettings = sessionSettings;

// Set an Init action customSessionSettings.setInitAction('CatalogList:catalogServiceNames=HTK_CAT~runFirst ExistingConf=yes');

// Or override the current Init action parameters

customSessionSettings.setInitAction(customSessionSettings.getInitAction().replace('HT K_CAT','CAT'));

// Set an Back action

customSessionSettings.setBackAction('Synchronize');

// Override the Quote Model Name

customSessionSettings.setQuoteModelName('CUSTOM_MODEL');

// Override the Quote Model Release

customSessionSettings.setQuoteModelRelease(99);

// Override the UI Layout

customSessionSettings.setUILayout('CUSTOM_LAYOUT');

// Override the Proposal UI Layout

customSessionSettings.setProposalUILayout('CUSTOM_PRINT_LAYOUT');

// Override the MappingSet Name

customSessionSettings.setMappingSetName('ACCOUNT');

// Override the User Group policy

customSessionSettings.setUserGroupPolicy(3);

// Return null if you do not want to override the Init or Back actions

return customSessionSettings;

}

}

The MappingSet Name can be overridden. However, only the MappingIN process will take into account the overridden value. The MappingOUT Process (CPQ -> SFDC synchronization) will get the MappingSet Name from the custom settings, not from the custom Apex class.

The local class must implement the interface

CameleonCPQ.CPQRequestFormatter.IMappingIN

And therefore provide an implementation for all of its methods

The following table lists the methods of the

interface CameleonCPQ.CPQRequestFormatter.IMappingIN.

Methods of interface CameleonCPQ.CPQRequestFormatter.IMappingIN

METHOD RETURNED TYPE DESCRIPTION
getCPQCustomSettings(Id QuoteId) List<CameleonCPQ.CPQRequestFormatter.Tuple> The Id in input of the method is giving the Force.com Id of the current Quote object. The output is a list of Tuple elements. A Tuple element is a code / value pair.
getCPQSessionSettings(Id entityId, CameleonCPQ.CPQRequestFormatter.CPQSessionSettings sessionSettings) CameleonCPQ.CPQRequestFormatter.CPQSessionSettings The Id in input of the method is giving the Force.com Id of the current Quote object. The sessionSettings are settings exchange with CPQ in the context of the actions The output is the set of CPQ session settings

When the local class has been deployed in the Force.com organization, activate it by setting its name (without any extension) in the PROS custom settings: