---
schema_version: "1"
id: dd5bc424bd576655dfa8c6810d2e6f823a356511143a4227010fc4fcd78d31bc
title: Relies On Product Relationships in Industries CPQ
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/industries/cme/guides/relies_on_capability.md
canonical_url: https://developer.salesforce.com/docs/industries/cme/guide/relies_on_capability.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: 915c7d5187a3c83bbbc767e8c12c66b00d1f25e1d3c8fb15a2f267f0b4fa8011
products:
  - Communications, Media, Energy and Utilities APIs (Managed Package)
  - Industries Developer Documentation
---
# Relies On Product Relationships in Industries CPQ

This section provides information about _Relies On_ relationship capability within Industries Configure, Price, Quote (CPQ).

Linear Relationship (“Relies On”) is an alternative product modeling approach to avoid the complexity of hierarchical product modeling.
In hierarchical product design, related products are grouped using a parent-child relationship, where child products are implicitly dependent on other products within the hierarchy.
In contrast, the Linear Design approach doesn't tightly couple products within a hierarchy. Instead, products are defined simply, with linear relationships indicating dependencies (also known as "Relies On") on other products.

**note:** - When a Relies On product relationship is configured between two products, adding the source product will not automatically add the related product.
- Attribute propagation for Relies On or linear relationships works only with Standard Cart APIs in Industries CPQ. When a Relies On relationship is set, the system creates an OrderProductRelationship during `postCartsItems.PostInvoke`. As a result, attribute propagation doesn't occur during the initial CPQ API call. However, any follow-up CPQ API call with `price=true` —such as `getCartsItems`—will propagate the attribute values. Customers can trigger this by adding a pricing call at the end of `postCartsItems.PostInvoke` or through a separate API call.

## Configure Relies On Relationship APIs

- Use Relies On Relationship APIs from the CPQ Cart for establishing the relationship between two related products defined using the Relies On type advanced rule.
- Relationships are then stored in the Order Product Relationship or Quote Product Relationship depending upon the type of Cart.
- To achieve this, perform the following.

  1. From the Developer Console, click **File > New > Apex Class**.
  2. Create a new class with the name `CPQAppHandlerHookImplementation`.
  3. Copy the following code and add it to newly created `CPQAppHandlerHookImplementation` class.

```
/**
 * @description :
 *      This Hook is written to add support for ESM's ReliesOn Relationships APIs on CPQ Cart
 *      In this class we are writing hook on postCartsItems.PostInvoke and deleteCartsItems.PreInvoke
 *      postCartsItems.PostInvoke => it covers services like, finding relationships that are eligible to be added based on XLI added to the cart
 *                                   it also links the qualified relationhips and discards the relationships which are already added
 *                                   it also does the verification if the Relationship's max cardinality is violated, only links when its not violated
 *
 *      deleteCartsItems.PreInvoke => this hook is used to delete the linked relationship if the corresponding XLI's relationship exits and is deleted
 *
 * @author : chandel.gupta@salesforce.com
 */
global with sharing class CPQAppHandlerHookImplementation implements vlocity_cmt.VlocityOpenInterface
{
    private vlocity_cmt.VOIInvoker invoker = vlocity_cmt.VOIInvoker.getInstance();
    private vlocity_cmt.VlocityOpenInterface appHandler = (vlocity_cmt.VlocityOpenInterface)invoker.invoke('B2BCmexAppHandler', 'debugCreate', null, null, null);

    global Boolean invokeMethod(String methodName, Map<String, Object> input, Map<String, Object> output, Map<String, Object> options)
    {
        try
        {
            System.debug('CPQAppHandlerHook method ' + methodName);

            if(methodName == 'postCartsItems.PostInvoke')
            {
                postCartsItemsPostInvoke(input,output,options);
            }
            else if(methodName == 'deleteCartsItems.PreInvoke')
            {
                deleteCartsItemsPreInvoke(input,output,options);
            }

            return true;
        }
        catch (Exception ex)
        {
            System.debug('--- Exception: ' + ex.getMessage());
            System.debug('--- Stack Trace: ' + ex.getStackTraceString());
            throw ex;
        }
    }

    public void deleteCartsItemsPreInvoke(Map<String, Object> input, Map<String, Object> output, Map<String, Object> options)
    {
        System.debug('deleteCartsItemsPreInvoke method ');
        Map<String, Object> inputDelRel = new Map<String, Object>();
        Map<String, Object> outputDelRel = new Map<String, Object>();
        Map<String, Object> optionsDelRel = new Map<String, Object>();

        String cartId = (String)input.get('cartId');
        String lineItemToDel = (String)input.get('id');
        String methodName = 'deleteOrderItemRelationships';

        inputDelRel.put('cartId', cartId);
        inputDelRel.put('lineItemId', lineItemToDel);

        //remote action invocation
        //vlocity_cmt.B2BCmexAppHandler appHandler = new vlocity_cmt.B2BCmexAppHandler();
        appHandler.invokeMethod(methodName, inputDelRel, outputDelRel, optionsDelRel);
        System.debug(JSON.serialize(outputDelRel));
    }

    public void postCartsItemsPostInvoke(Map<String, Object> input, Map<String, Object> output, Map<String, Object> options)
    {
        System.debug('postCartsItemsPostInvoke method ');

        Map<String, Object> inputRel = new Map<String, Object>();
        Map<String, Object> outputRel = new Map<String, Object>();
        Map<String, Object> optionsRel = new Map<String, Object>();

        String cartId = (String)input.get('cartId');
        String methodName = 'getCartProductRelationship';

        inputRel.put('cartId', cartId);
        //inputRel.put('source', 'ENTERPRISE_CART');
        //inputRel.put('rootItemId', null);
        //inputRel.put('getOnlyRootItems', 'true/false');

        //remote action invocation
        //vlocity_cmt.B2BCmexAppHandler appHandler = new vlocity_cmt.B2BCmexAppHandler();
        appHandler.invokeMethod(methodName, inputRel, outputRel, optionsRel);
        System.debug(JSON.serialize(outputRel));

        Map<Id, OrderItem> xlisMap = new Map<Id, OrderItem>([SELECT Id, vlocity_cmt__AssetReferenceId__c, vlocity_cmt__Product2Id__c, vlocity_cmt__Product2Id__r.Name FROM OrderItem WHERE Order.Id = :cartId]);
        List<vlocity_cmt__ProductRelationship__c> prodRelRules = (List<vlocity_cmt__ProductRelationship__c>) outputRel.get('ProductRelationship');


        // map to hold record of existing quantity of the relationships that are already linked
        Map<String, Integer> existingOPRelQtyMap = new Map<String, Integer>();

        Map<String,vlocity_cmt__OrderItemRelationship__c> orderProdRelsMap = new Map<String,vlocity_cmt__OrderItemRelationship__c>();

        //converting List of XLI Relationships to Map, and forming Map that holds quantity of relationships that are already added
        for(vlocity_cmt__OrderItemRelationship__c opr : (List<vlocity_cmt__OrderItemRelationship__c>)outputRel.get('OrderProductRelationship'))
        {
            orderProdRelsMap.put(opr.vlocity_cmt__OrderItemId__c + ';' + opr.vlocity_cmt__RelatedOrderItemId__c, opr);
            existingOPRelQtyMap.put(opr.vlocity_cmt__OrderItemId__c + ';' + opr.vlocity_cmt__RelatedOrderItemId__r.Product2Id + ';' + opr.vlocity_cmt__ProductRelationshipIdentifier__c, (opr.vlocity_cmt__RelatedOrderItemId__r.Quantity).intValue());
        }
        System.debug(JSON.serialize(orderProdRelsMap));
        System.debug(JSON.serialize(existingOPRelQtyMap));

        Map<Id,List<SObject>> xliToRelProdListMap = new Map<Id,List<SObject>>();
        Map<Id, Object> relProdIdToProdRelMap = new Map<Id, Object>();

        // map to hold record of the quantity of the relationships that are to be linked
        Map<String, Integer> toBeLinkedOPRelQtyMap = new Map<String, Integer>();
        Map<String, String> orderItemRelProdToRuleIdMap = new Map<String, String>();

        for(Id xliId : xlisMap.keySet())
        {
            OrderItem xli = (OrderItem) xlisMap.get(xliId);
            for(vlocity_cmt__ProductRelationship__c prodRelRule : prodRelRules)
            {
                // forming "One to Many" relationship between XLI and Related Products
                if(xli.vlocity_cmt__Product2Id__c == prodRelRule.vlocity_cmt__Product2Id__c)
                {
                    if(xliToRelProdListMap.get(xli.Id) != null)
                    {
                        List<SObject> prodRelRuleList = (List<SObject>) xliToRelProdListMap.get(xli.Id);
                        prodRelRuleList.add(prodRelRule);
                    }
                    else
                    {
                        List<SObject> prodRelRuleList = new List<SObject>();
                        prodRelRuleList.add(prodRelRule);
                        xliToRelProdListMap.put(xli.Id, prodRelRuleList);
                    }

                    // map to hold record of the quantity of the relationships that are to be linked => will be used to validate cardinality violation
                    toBeLinkedOPRelQtyMap.put(xliId + ';' + prodRelRule.vlocity_cmt__RelatedProductId__c + ';' + prodRelRule.Id, 1);
                    orderItemRelProdToRuleIdMap.put(xliId + ';' + prodRelRule.vlocity_cmt__RelatedProductId__c, prodRelRule.Id);
                }

                // forming relationship between RelatedProduct and Related XLI to be used for Linking XLIs
                if(xli.vlocity_cmt__Product2Id__c == prodRelRule.vlocity_cmt__RelatedProductId__c )
                {
                    relProdIdToProdRelMap.put(prodRelRule.vlocity_cmt__RelatedProductId__c, new Map<String, Object>
                    {
                        prodRelRule.Id => new List<Map<String, Object>>{
                        new Map<String, Object>{
                            'id' => xli.Id,
                            'assetReferenceId' => xli.vlocity_cmt__AssetReferenceId__c,
                            'relatedProductId' => prodRelRule.vlocity_cmt__RelatedProductId__c
                        }
                    }});
                }
            }
        }

        // return if no related lineitem to link to in the cart
        if(relProdIdToProdRelMap.size() <= 0) return;

        // forming map "maxCardinalityMap" with key and value as => "orderItemId;relatedProdId;ruleId => max_qty", to know the max cardinality the rule supports
        // e.g 802xx000001ncoeAAA;01txx0000006i8wAAA;a3Sxx0000004C92EAE => 6
        Map<String, Integer> maxCardinalityMap = new Map<String, Integer>();

        Map<String, List<Object>> lineItemListOfLinkagesMap = new Map<String, List<Object>>();

        for(Id xliId : xliToRelProdListMap.keySet())
        {

            List<vlocity_cmt__ProductRelationship__c> xliRelProdList = (List<vlocity_cmt__ProductRelationship__c>) xliToRelProdListMap.get(xliId);

            for(vlocity_cmt__ProductRelationship__c xliRelProd : xliRelProdList)
            {
                if(relProdIdToProdRelMap.get(xliRelProd.vlocity_cmt__RelatedProductId__c) != null)
                {
                    if(lineItemListOfLinkagesMap.get(xliId) != null)
                    {
                        List<Object> xliLinkagesList = (List<Object>) lineItemListOfLinkagesMap.get(xliId);
                        xliLinkagesList.add(relProdIdToProdRelMap.get(xliRelProd.vlocity_cmt__RelatedProductId__c));
                    }
                    else
                    {
                        List<Object> xliLinkagesList = new List<Object>();
                        xliLinkagesList.add(relProdIdToProdRelMap.get(xliRelProd.vlocity_cmt__RelatedProductId__c));
                        lineItemListOfLinkagesMap.put(xliId, xliLinkagesList);
                    }
                }

                //Map<String, Object> relationshipMap = (Map<String, Object>) relationship;
                String orderItemId = xliId;
                String relatedProdId = (String) xliRelProd.vlocity_cmt__RelatedProductId__c;
                String ruleId = (String) xliRelProd.Id;
                Decimal maxQuantity = (Decimal) xliRelProd.vlocity_cmt__MaxQuantity__c;

                maxCardinalityMap.put(orderItemId + ';' + relatedProdId + ';' + ruleId, maxQuantity.intValue());
            }
        }

        System.debug(JSON.serialize(xliToRelProdListMap));
        System.debug(JSON.serialize(relProdIdToProdRelMap));
        System.debug(JSON.serialize(lineItemListOfLinkagesMap));

        System.debug(JSON.serialize(maxCardinalityMap));
        System.debug(JSON.serialize(existingOPRelQtyMap));
        System.debug(JSON.serialize(toBeLinkedOPRelQtyMap));
    	System.debug(JSON.serialize(orderItemRelProdToRuleIdMap));

        // linking LineItems based on defined "ReliesOn" relationships
        for(Id xliId : lineItemListOfLinkagesMap.keySet())
        {
            Map<String, Object> linkItemInput = new Map<String, Object>();
            Map<String, Object> linkItemOutput = new Map<String, Object>();

            for(Object xliLinkages : lineItemListOfLinkagesMap.get(xliId))
            {
                Id linkedRelXliId = (Id) ((Map<String, Object>)((List<Object>) JSON.deserializeUntyped((String)JSON.serialize(((Object) ((Map<String, Object>) JSON.deserializeUntyped((String)JSON.serialize(xliLinkages))).values().get(0))))).get(0)).get('id');
                Id linkedRelProdId = (Id) ((Map<String, Object>)((List<Object>) JSON.deserializeUntyped((String)JSON.serialize(((Object) ((Map<String, Object>) JSON.deserializeUntyped((String)JSON.serialize(xliLinkages))).values().get(0))))).get(0)).get('relatedProductId');
                Id ruleId = orderItemRelProdToRuleIdMap.get(xliId + ';' + linkedRelProdId);

                // Add relationship only when relationship is not already linked also check if the Rule's max cardinality is violated or not,
                // link only when it's not violated, else throw soft warning message in the response
                if(!(orderProdRelsMap.get(xliId + ';' + linkedRelXliId) != null))
                {
                    String key =  xliId + ';' + linkedRelProdId + ';' + ruleId;
                    Integer maxCardinality = maxCardinalityMap.get(key) == null ? 0 : maxCardinalityMap.get(key);
                    Integer existingCardinality = existingOPRelQtyMap.get(key) == null ? 0 : existingOPRelQtyMap.get(key);
                    Integer toBeAddedCardinality = toBeLinkedOPRelQtyMap.get(key) == null ? 0 : toBeLinkedOPRelQtyMap.get(key);

                    System.debug('maxCardinality => ' + maxCardinality + ' existingCardinality => '+ existingCardinality + 'toBeAddedCardinality => ' + toBeAddedCardinality);

                    if(existingCardinality + toBeAddedCardinality > maxCardinality)
                    {
                        System.debug('Max cardinality limit for the rule : ' + ruleId + ' is violated for OrderItem : ' + xliId + ' and Related OrderItem' + linkedRelXliId);
                        vlocity_cmt.JSONResult result = (vlocity_cmt.JSONResult) output.get('result');

                        String message = 'Max cardinality limit for the rule : ' + ruleId + ' is violated for OrderItem : ' + xliId + ' and Related OrderItem : ' + linkedRelXliId;
                        //vlocity_cmt.JSONMessage jsonMessage = new vlocity_cmt.JSONMessage('422', vlocity_cmt.JSONMessage.Severity.WARN, message);
                        //result.messages.add(jsonMessage);
                    }
                    else
                    {
                        System.debug('Qualified to be added : ' + xliId + ';' + linkedRelXliId );
                        Map<String, Object> lineItemLinkageMap = new Map<String, Object>{xliId => xliLinkages};

                        // Setting remote parameters
                        linkItemInput.put('cartId', cartId);
                        linkItemInput.put('LineItemLinkage', lineItemLinkageMap);

                        //remote action invocation
                        appHandler.invokeMethod('linkProductsForOrder', linkItemInput, linkItemOutput, new Map<String, Object>());
                    }
                }
                else {
                    System.debug('Disqualified to be added, as relationship already exists : ' + xliId + ';' + linkedRelXliId );
                }
            }
        }
    }
}


```

1. Create a new interface with the name `CpqAppHandlerHookInterface` and with the following configuration.

![CPQ Handler](https://developer.salesforce.com/docs/media/CPQAppHandlerHookInterface.png "CPQ Handler")

The `CPQAppHandlerHookImplementation` class invokes the following methods.

- **`postCartsItems.PostInvoke`**
  This method is invoked whenever a new offer is added to the cart.
  Based on the newly added product and already existing products in the cart, following variables are important.

  - orderProdRelsMap: Contains existing OrderProductRelationship that are already linked and ProductRelationship list that can be linked.
  - xliToRelProdListMap: Contains against each lineitem, which all "Related Product(s)" exist in the Cart.
  - lineItemListOfLinkagesMap: Contains the relationships that are eligible to be added if not already added.

- **`deleteCartsItems.PreInvoke`**
  This method is invoked whenever the LineItem is deleted, and if that LineItem contains any added Relies On Relationship in the cart already, then respective relationship is deleted.
  - lineItemToDel: Contains the LineItem on which delete operation is invoked and for which existing relationship(s) needs to be deleted.

### Remote Methods

Following remote methods are used.

#### GetCartProductRelationship

This method fetches relationships based on XLIs present in the Order/Quote.
This response contains the following.

- OrderProductRelationship: Linked Order Product Relationships
- QuoteProductRelationship: Linked Quote Item Relationships
- ProductRelationship: List of eligible Relies On Relationships that can be linked

```
Map<String, Object> input = new Map<String, Object>();
Map<String, Object> output = new Map<String, Object>();
Map<String, Object> options = new Map<String, Object>();
// Setting remote parameters
String methodName = 'getCartProductRelationship';
input.put('cartId','801xx000003GssBAAS');
//input.put('getOnlyRootItems', 'true/false'); => optional
//remote action invocation
vlocity_cmt.B2BCmexAppHandler appHandler = new vlocity_cmt.B2BCmexAppHandler();
// output map will contains the response
appHandler.invokeMethod(methodName, input, output, options);
```

#### LinkProducts

This method inks relationships between XLIs and related XLIs present in the Order/Quote.
This response contains the linked relationship, based on the type of Cart (Order/Quote).

- Method Name for Order type Cart: `linkProductsForOrder`
- Method Name for Quote type Cart: `linkProducts`

**Sample Request**

```
{
    "LineItemLinkage": {
        "802SG0000030Ctt": { // LineItem => order product with rule
        "a4FSG00000IPkII": [    // Product Relationship Rule Id
           {
               "id": "802SG0000030Ctv", // LineItem => order product with rule => aplies to
               "assetReferenceId": "d30901b4-08eb-46c2-87be-4c51338b9156"
           }
        ]
     }
  },
  "cartId": "801SG00000Dq4w5"
}
```

**Sample Response**

```
{
"Response": {
"error": "OK",
"errorCode": "INVOKE-200",
"relationsDeleted": {},
"relationsAdded": [
{
"devopsimpkg19__RelatedOrderItemId__r": {
"Product2": {
"RecordTypeId": "012SG000001MSuGYAW",
"Id": "01tSG000000NCtLYAW",
"Name": "PRODUCT-ABP-CHILD2",
"attributes": {
"url": "/services/data/v61.0/sobjects/Product2/01tSG000000NCtLYAW",
"type": "Product2"
   }
},
"Id": "802SG0000030CtvYAE",
"Product2Id": "01tSG000000NCtLYAW",
"attributes": {
"url": "/services/data/v61.0/sobjects/OrderItem/802SG0000030CtvYAE",
"type": "OrderItem"
    }
},
"Id": "a3cSG0000007bNpYAI",
"devopsimpkg19__RelatedAssetReferenceId__c": "d30901b4-08eb-46c2-87be-4c51338b9156",
"devopsimpkg19__RelationshipType__c": "ReliesOn",
"devopsimpkg19__RelatedOrderItemId__c": "802SG0000030CtvYAE",
"devopsimpkg19__OrderItemId__c": "802SG0000030CttYAE",
"devopsimpkg19__OrderId__c": "801SG00000Dq4w5YAB",
"devopsimpkg19__ProductRelationshipIdentifier__c": "a4FSG00000IPkII",
"Name": "a3cSG0000007bNp",
"attributes": {
"url": "/services/data/v61.0/sobjects/devopsimpkg19__OrderItemRelationship__c/a3cSG0000007bNpYAI",
"type": "devopsimpkg19__OrderItemRelationship__c"
      }
   }
  ]
  }
}
```

#### GetAddedRelatedLineItemQuantity

This method fetches already linked rules quantity for a given XLI present in the Order/Quote.
This response contains the following.

- Method Name for Order type Cart: `getAddedRelatedLineItemQuantityForOrder`
- Method Name for Quote type Cart: `getAddedRelatedLineItemQuantity`

```
Map<String, Object> input = new Map<String, Object>();
Map<String, Object> output = new Map<String, Object>();
Map<String, Object> options = new Map<String, Object>();
// Setting remote parameters
String methodName = 'getAddedRelatedLineItemQuantityForOrder';
input.put('cartId','801xx000003GssBAAS');
input.put('orderItemId', 'xliId'); //802SG0000030CsHYAU
//remote action invocation
vlocity_cmt.B2BCmexAppHandler appHandler = new vlocity_cmt.B2BCmexAppHandler();
appHandler.invokeMethod(methodName, input, output, options);
```

Response contains the information same as what is inserted in sObjects `OrderProductRelationship` / `QuoteProductRelationship`.

```
{"ruleIdToQuantityMap":{"a3Sxx0000004EHIEA2":1.0}}
```

#### DeleteOrderItemRelationship

This method deletes the already linked relationships when XLI is deleted from the Order/Quote.
This response contains the following.

- Method Name for Order type Cart: `deleteOrderItemRelationships`
- Method Name for Quote type Cart: `deleteQuoteLineItemRelationships`

```
Map<String, Object> input = new Map<String, Object>();
Map<String, Object> output = new Map<String, Object>();
Map<String, Object> options = new Map<String, Object>();
// Setting remote parameters
String methodName = 'deleteOrderItemRelationships';
input.put('cartId','801xx000003GssBAAS');
input.put('lineItemId', 'xliId'); //802SG0000030CsHYAU

//remote action invocation
vlocity_cmt.B2BCmexAppHandler appHandler = new vlocity_cmt.B2BCmexAppHandler();
appHandler.invokeMethod(methodName, input, output, options);
```
