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:
- Implement the custom plugin by using the below code to create a new class.
- On the Zuora Config tab, click Component Registration.
- On the Component Registration page, click Edit for the SelectProduct component.
- In the Update component section, enter the plugin class name, RulesEngineTest, in the Class Name field of the Quote Rule Engine Plugin.
- Click Update.
- 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); } }