Skip to main content

Quote Rules Engine Plugin Example

Zuora

Quote Rules Engine Plugin Example

This article describes the process of creating a sample custom Quote Rules Engine Plugin on the SelectProduct component

To implement and register a new custom Quote Rules Engine Plugin on the SelectProduct component:

  1. Implement the custom plugin by using the below code to create a new class.
  2. On the Zuora Config tab, click Component Registration.
  3. On the Component Registration page, click Edit for the SelectProduct component. 
  4. In the Update component section, enter the plugin class name, RulesEngineTest, in the Class Name field of the Quote Rule Engine Plugin.
  5. Click Update.
  6. When you create or edit a quote, the rules created in the below sample code will be applied to your product and rate plan selection.

Code Sample

Use the following code to implement the custom plugin in this example.

global class RulesEngineSampleController implements
  zqu.ZQuoteRulesEngine.QuoteRulesEnginePlugin {
 
  public static Boolean runValidationRules(zqu__Quote__c quote,
                                    List < zqu.ZChargeGroup > zcgs,
                                    List < zqu.ZQuoteRulesEngine.ChangeLog > logs) {
 
    // Validate rate plans only after quote has been created.
    if(quote.Id != null){
      Set<Id> ratePlanIds = new Set<Id>();
      Map<String,String> ratePlanNameMap = new Map<String,String>();
      
      // Get 3 rate plans randomly in the sample code
      // Replace them to your real rate plan ids
      String ratePlanA = zcgs.size()>1 ? zcgs.get(1).productRatePlanId : '';
      String ratePlanB = zcgs.size()>2 ? zcgs.get(2).productRatePlanId : '';
      String ratePlanC = zcgs.size()>0 ? zcgs.get(0).productRatePlanId : '';
       
      for(zqu.ZChargeGroup zcg : zcgs){
        ratePlanIds.add(zcg.productRatePlanId);
        ratePlanNameMap.put(zcg.productRatePlanId, zcg.ratePlanName);
      }
       
      // Case1 : Rate plan A and B cannot be added together
      if(!Test.isRunningTest() && ratePlanIds.contains(ratePlanA) && ratePlanIds.contains(ratePlanB)){
        zqu.ZQuoteRulesEngine.ChangeLog log = new zqu.ZQuoteRulesEngine.ChangeLog();
        log.description = 'The following rate plans cannot be added together : ' +
          ratePlanNameMap.get(ratePlanA) + ',' + ratePlanNameMap.get(ratePlanB);
        logs.add(log);
        return false;
      }
      
      // Case2 : Rate plan C must be added to the quote, cannot remove it
      if(!ratePlanIds.contains(ratePlanC)){
        zqu.ZQuoteRulesEngine.ChangeLog log = new zqu.ZQuoteRulesEngine.ChangeLog();
        log.description = 'The following rate plan must be added : ' + ratePlanC;
        logs.add(log);
        return false;
      }
    }
     
    // Validate quote itself
    // Case3 : Quote start date cannot be today
    if(quote.zqu__StartDate__c == Date.today()){
      zqu.ZQuoteRulesEngine.ChangeLog log = new zqu.ZQuoteRulesEngine.ChangeLog();
      log.description = 'Quote start date cannot be today.';
      logs.add(log);
      return false;
    }
     
    return true;
  }
   
 
 public static void runPriceRules(List < zqu.ZChargeGroup > zcgs,
                                 List < zqu.ZQuoteRulesEngine.ChangeLog > logs) {
     
     for(zqu.ZChargeGroup zcg : zcgs){
       List<zqu__QuoteRatePlanCharge__c> chargeObjectList = new List<zqu__QuoteRatePlanCharge__c>();
       List<zqu.zCharge> zChargeList = new List<zqu.zCharge>();
       for(zqu.zCharge zc : zcg.zCharges){
         
         // Create zqu__QuoteRatePlanCharge__c object instance from zCharge
         zqu__QuoteRatePlanCharge__c qrpc = new zqu__QuoteRatePlanCharge__c();
         qrpc.Name = zc.Name;
         qrpc.zqu__ListPrice__c = Decimal.valueOf(Test.isRunningTest() ? '100' : zc.LIST_PRICE);
         qrpc.zqu__ListTotal__c = Decimal.valueOf(Test.isRunningTest() ? '100' : zc.LIST_TOTAL);
         qrpc.zqu__Quantity__c =
           zc.isQuantityEditable ? Decimal.valueOf(zc.QUANTITY) : null;
         qrpc.zqu__EffectivePrice__c = zc.isEffectivePriceEditable ?
           Decimal.valueOf(zc.EFFECTIVE_PRICE) : null;
         qrpc.zqu__Discount__c =
           zc.isDiscountEditable ? Decimal.valueOf(zc.DISCOUNT) : null;
         qrpc.zqu__Total__c =
           zc.isTotalEditable ? Decimal.valueOf(zc.TOTAL) : null;
         qrpc.zqu__IncludedUnits__c = zc.IsIncludedUnitsEditable ?
          Decimal.valueOf(zc.INCLUDED_UNITS) : null;
         qrpc.zqu__ProductRatePlanCharge__c = zc.PRODUCT_RATE_PLAN_CHARGE_SFDC_ID;
        
         chargeObjectList.add(qrpc);
         zChargeList.add(zc);
       }
              
       // Put all changed field names into map :
       // Map<charge.zqu__productrateplancharge__c,>>
       Map<String,List<String>> changedFieldMap = new Map<String,List<String>>();

       for(Integer index = 0; index < chargeObjectList.size(); index++){
         SObject charge = chargeObjectList.get(index);
         zqu.zCharge zc = zChargeList.get(index);

         String productRatePlanChargeId = String.valueOf(charge.get(
           'zqu__ProductRatePlanCharge__c'));

         // Modify List Price
         if(Boolean.valueOf(zc.isListPriceEditable())){
           charge.put('zqu__ListPrice__c', 40);
           zc.overRideListPrice(40);
                  
           // Store changed field name for List Price
           if(!changedFieldMap.containsKey(productRatePlanChargeId)){
             changedFieldMap.put(productRatePlanChargeId, new List<String>());
           }
           changedFieldMap.get(productRatePlanChargeId).add(
             'LIST_PRICE');

           // Add change log
           zqu.ZQuoteRulesEngine.ChangeLog log = new zqu.ZQuoteRulesEngine.ChangeLog();
           log.description = String.valueOf(charge.get('Name')) + ' : ' +
             'List Price is changed to 40';
           logs.add(log);
          }
                   
           // Store changed field name for Discount
           if(!changedFieldMap.containsKey(productRatePlanChargeId)){
             changedFieldMap.put(productRatePlanChargeId, new List<String>());
           }
           changedFieldMap.get(productRatePlanChargeId).add(
             zqu.ZQuoteRulesEngine.PRICE_FIELD_DISCOUNT);

           // Add change log
           zqu.ZQuoteRulesEngine.ChangeLog log = new zqu.ZQuoteRulesEngine.ChangeLog();
           log.description = String.valueOf(charge.get('Name')) + ' : ' +
             zqu.ZQuoteRulesEngine.PRICE_FIELD_DISCOUNT + ' is changed to 50';
           logs.add(log);
         }
         zqu.zQuoteUtil.updateZChargeGroupFromSObject(zcg, chargeObjectList,
         changedFieldMap, '');
       }
       
       // Update zChargeGroup from charge object list
       //zqu.zQuoteUtil.updateZChargeGroupFromSObject(zcg, chargeObjectList,
         //changedFieldMap, '');
   } 

  public static Map < String, List < String >> runProductRules(zqu__Quote__c quote,
                                    List < String > ratePlanIds,
                                    List < zqu.ZQuoteRulesEngine.ChangeLog > logs) {
 
    Map<String,List<String>> relatedRatePlanIdMap = new Map<String,List<String>>();
    relatedRatePlanIdMap.put(zqu.ZQuoteRulesEngine.PRODUCT_RULE_TYPE_ADDED_ENABLED,
      new List<String>());
    relatedRatePlanIdMap.put(zqu.ZQuoteRulesEngine.PRODUCT_RULE_TYPE_ADDED_DISABLED,
      new List<String>());
    relatedRatePlanIdMap.put(zqu.ZQuoteRulesEngine.PRODUCT_RULE_TYPE_REMOVED_ENABLED,
      new List<String>());
    
    // Replace the follwing rate plans to your real rate plan ids
    String ratePlanA = 'ratePlanAId';
    String ratePlanB = 'ratePlanBId';
    String ratePlanC = 'ratePlanCId';
    
    Set<String> existingRatePlans = new Set<String>();
    if(ratePlanIds != null){
      existingRatePlans.addAll(ratePlanIds);
    }
    
    // Change log
    zqu.ZQuoteRulesEngine.ChangeLog log;
    
    // Case 1 : If rate plan A is required and has not been added to quote,
    // add it to ZQuoteRulesEngine.PRODUCT_RULE_TYPE_ADDED_DISABLED,
    // the rate plan A will be added to quote automatically and cannot be removed
    if(!existingRatePlans.contains(ratePlanA)){
      relatedRatePlanIdMap.get(
        zqu.ZQuoteRulesEngine.PRODUCT_RULE_TYPE_ADDED_DISABLED).add(ratePlanA);
      // Add log
      log = new zqu.ZQuoteRulesEngine.ChangeLog();
      log.description = 'Required Rate plan ' + ratePlanA + ' is added automatically.';
      logs.add(log);
    }
     
    // Case 2 : If add rate plan B to
    // ZQuoteRulesEngine.PRODUCT_RULE_TYPE_ADDED_ENABLED,
    // the rate plan B will be added to quote automatically and can be removed
    relatedRatePlanIdMap.get(
      zqu.ZQuoteRulesEngine.PRODUCT_RULE_TYPE_ADDED_ENABLED).add(ratePlanB);
    
    // Add log
    log = new zqu.ZQuoteRulesEngine.ChangeLog();
    log.description = 'Rate plan ' + ratePlanB + ' is added automatically.';
    logs.add(log);
     
    // Case 3 : If rate plan C has been added to quote,
    // add it to ZQuoteRulesEngine.PRODUCT_RULE_TYPE_REMOVED_ENABLED,
    // the rate plan C will be removed from quote automatically
    if(existingRatePlans.contains(ratePlanC)){
      relatedRatePlanIdMap.get(
        zqu.ZQuoteRulesEngine.PRODUCT_RULE_TYPE_REMOVED_ENABLED).add(ratePlanC);
      // Add log
      log = new zqu.ZQuoteRulesEngine.ChangeLog();
      log.description = 'Rate plan ' + ratePlanC + ' is removed automatically.';
      logs.add(log);
    }
    return relatedRatePlanIdMap;
  }  
}       

Test Class for Quote Rules Engine Plugin

The following is a APEX test class code sample for Quote Rules Engine Plugin. You can use the test class to achieve code coverage on the plugin in Salesforce.

@isTest
public class RulesEngineSampleControllerTest {
@isTest
    static void testRunValidationRules() {
        List<zqu.zChargeGroup> lstzcg = new List<zqu.zChargeGroup>();
final Product2 testProduct =
zqu.ZQTestDataSetup.prepareFeatureProduct();
        zqu__ProductRatePlan__c ratePlan = [
            SELECT Id, Name
            FROM zqu__ProductRatePlan__c
            WHERE zqu__ProductRatePlan__c.zqu__Product__r.Id = :testProduct.Id AND
Name = 'Plan for Volume charges'
            LIMIT 1
        ];
        zqu__Quote__c quote2 =
zqu.ZQTestDataSetup.prepareNewSubscriptionQuote(true);
        zqu__QuoteProductFeature__c quoteProductFeature;
        zqu.zChargeGroup zcg = zqu.zQuoteUtil.getChargeGroup(quote2.Id,
ratePlan.Id);
        lstzcg.add(zcg);
        List < zqu.ZQuoteRulesEngine.ChangeLog > logs = new List <
zqu.ZQuoteRulesEngine.ChangeLog >();
        RulesEngineSampleController.runValidationRules(quote2,lstzcg,logs);
        RulesEngineSampleController.runPriceRules(lstzcg,logs);
        RulesEngineSampleController.runProductRules(quote2, new List<String>
{ratePlan.Id}, logs);
    }
}