Skip to main content

Custom Action Plugin


Custom Action Plugin

Use the Custom Action Plugin to configure additional actions you want to trigger via Zuora Rules Engine. The Custom Action Plugin interface class, zqu.CustomActionPlugin, contains the following interface methods:

  • perform
  • isUpdateAction
  • isAddRemoveAction
  • isValidateAction

Perform Method

In the perform method, define the logic this custom action will execute. See the sample code below for an example implementation of the perform method.

The method takes the following input parameters.


Data Type


masterObject zqu.DataObject Contains the record that the rule is currently running against and its related data. This will generally be a Quote record.
attributes Map<String, Object> A map of parameters that can be defined by an admin when configuring a custom action.
logs String[] A list of strings that get printed to the Rules Engine log when the rules engine executes the action.

Rule Types Methods

When initiating the Rules Engine programmatically, you have an option to specify which types of actions to run. For example, calling the Rules Engine with the Rule Type of VALIDATION will only evaluate rules with a Validation action.

By default, the Rules Engine runs all actions.

When defining a custom action, set the following methods to return true to specify the types of this action.


Rule Types that Can Invoke this Action








Using the following constructors, you can instantiate different types of DataObjects to add new data during a custom action.

Contructor Description

new ZChargeGroupDataObject
(DataObject dataQuote, ID productRatePlanId)

new ZChargeGroupDataObject

Creates a new ChargeGroup, retrieved through the zqu.zQuoteUtil.getChargeGroup global method.

QuoteRatePlan fields can be retrieved from this record, even if it has not yet been saved to the database.

Charge objects can be retrieved from an instance of this object using:


new PlaceholderChargeGroupDataObject
(ID productRatePlanId)

Represents a charge group to be added after rules execution completes.

Charge and Plan details are not accessible from this instance, but because it does not call getChargeGroups, it runs more quickly and makes no SOQL queries.

new CommittedDataObject
(SObject obj)

Represents an SObject record inserted into the Database.

Not used for Product Actions.

Sample Code

The below sample code implements a custom action that updates charges, removes and adds rate plans, and validates the quote.

global with sharing class MyCustomActionPlugin implements zqu.CustomActionPlugin{
  global void perform(zqu.DataObject masterObject, 
      Map < String, Object > attributes, String[] logs){
      // Read values from Quote Object
      String quoteId = (String)masterObject.get('Id');
      logs.add('QuoteId=' + quoteId);
      // Read values set in Rule Configuration
      String attributeMsg = (String)attributes.get('msg');
      logs.add('Attribute Message=' + attributeMsg);
      // Read values from child objects.
      List < zqu.DataObject > plans = 
      logs.add('# of plans: ' + plans.size() );
      // Updating Charges and Tiers
      List < zqu.DataObject > charges = 
      logs.add('# of charges: ' + plans.size() );
      Decimal chargeListPrice = 10, tierListPrice = 10, tierDiscount = 50;
      for (zqu.DataObject charge : charges) {
        // Update the list price at the charge level 
        // for 'Flat Fee Pricing' charge type.
        if (charge.get('zqu__Model__c') == 'Flat Fee Pricing') {
            charge.put('zqu__ListPrice__c', chargeListPrice);
            logs.add(​​​​'Updating list price of charge (' + 
              charge.get('Name') + ') to ' + 
              chargeListPrice + '.');
            chargeListPrice += 10;

        // Update the tier details, List Price, Discount, Effective Price.
        if (charge.get('zqu__ChargeType__c') == 'Recurring'
           && (charge.get('zqu__Model__c') == 'Tiered Pricing' 
           || charge.get('zqu__Model__c') == 'Volume Pricing')) {

           // Get the list of tiers for the charge object and 
           ​​​​​​​// modify the tier information.
           zqu.ZChargeDataObject chargeObject = (zqu.ZChargeDataObject) charge;
           List<zqu__QuoteCharge_Tier__c> tiers = 
        ​​​​​​​     chargeObject.getCharge().chargeTiersObjects;
           if (tiers != null && tiers.size() > 0) {
           for (zqu__QuoteCharge_Tier__c tier : tiers) {
             tier.zqu__Price__c = tierListPrice;
             tier.zqu__Discount__c = tierDiscount;
             tier.zqu__Effective_Price__c = (tierListPrice / 2);
             tierListPrice += 10.00;
           logs.add('Updating the tiers of charge (' + charge.get('Name') + ').');

           // Populate the changed tier informations.
      // Removing Charge Group
      for(zqu.DataObject plan : plans){
          String targetProduct = 'Orange Box';
          if((String) plan.get('zqu__QuoteProductName__c') == targetProduct){
              logs.add('You have a ' + targetProduct + ' plan.');
              logs.add(targetProduct + ' Removed.');
      // Add Product (Adds immediately, allows access to Charge details)
      zqu.DataObject addedPlan = 
          ​​​​​​new zqu.ZChargeGroupDataObject(masterObject, '01t36000002i9ki');
      // Set ProductRatePlan reference.
      zqu__ProductRatePlan__c prp = new zqu__ProductRatePlan__c(Id=productRatePlanId);
      zqu.DataObject dataProductRatePlan = new zqu.CommittedDataObject(prp);
      addedPlan.putParent('zqu__ProductRatePlan__c', dataProductRatePlan);
      masterObject.addChild('zqu__QuoteRatePlan__c', addedPlan);
      // Add Product (Adds after rules are run, does not allow access 
      // to Charge details)
      zqu.DataObject toBeAddedPlan = 
          new zqu.PlaceholderChargeGroupDataObject('01t36000002i9ki');
      masterObject.addChild('zqu__QuoteRatePlan__c', toBeAddedPlan);
      // Validation Error
      if(plans.size() > 2){
          throw new zqu.CustomValidationException(
              'You cannot have more than two plans on a quote.');
  global Boolean isUpdateAction() { return true; }
  global Boolean isAddRemoveAction() { return true; }
  global Boolean isValidateAction() { return true; }