Implement a custom quote rules engine plugin
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.
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;
}
}
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);
}
}