Skip to main content

Use the Product Catalog to Add, Update, and Delete Charges

Zuora

Use the Product Catalog to Add, Update, and Delete Charges

This article describe how to use the Product Catalog to add, update, and delete charges.

Global Classes

For managing charges, use the following global classes

  • zChargeThe zCharge global class represents a single charge in Zuora Quotes. It can be new, saved, or amended.
  • zChargeGroupThe zChargeGroup global class represents a single group of charges under a particular rate plan. All global methods operate on this global class.

Global Methods to Query Charges

Global Method: getChargeGroups (quoteId)

This method supports querying the following scenarios:

  • New charge groups: Returns charge groups that were added to this New Subscription quote. The returned charge group has the type TYPE_SUBSCRIPTION_ADDED.
  • Original subscription charge groups: Returns all charge groups from an Amendment quote combined with the original subscription in Z-Billing that is being amended. The returned charge will have one of the TYPE_AMENDMENT_* types. See "Types and Quotes that can be Queried," below, for more information.
  • Renewed subscription charge groups: Returns all renewal charges, including all charges that have been added or upgraded with a quote of type Renewal. The returned charge will have one of the TYPE_RENEWAL_* types. See "Types and Quotes that can be Queried," below, for more information.

Global Method: getChargeGroup (quoteID, productrateplanId)

This method supports querying when the charge group is not tied to a quote. The charge group will be created from the product catalog records (ZProduct, ProductRatePlan and RatePlanCharges).

Types and Quotes that can be Queried

This table describes the different zChargeGroup types and quotes that can be retrieved by the getChargeGroups (quoteId) and getChargeGroup (quoteID, productrateplanId) global methods. The zChargeGroup type is a global variable exposed in the zChargeGroup data structure.

 

  Quote Subscription Type
zChargeGroupType New Amendment Renewal
TYPE_PRODUCT_NEW      
TYPE_SUBSCRIPTION_ADDED      
TYPE_AMENDMENT_ORIGINAL   Yes  
TYPE_AMENDMENT_NEWPRODUCT   Yes  
TYPE_AMENDMENT_UPDATEPRODUCT   Yes  
TYPE_AMENDMENT_REMOVEPRODUCT   Yes  
TYPE_RENEWAL_RENEWED     Yes
TYPE_RENEWAL_NEWPRODUCT     Yes
TYPE_RENEWAL_UPDATEPRODUCT     Yes
TYPE_RENEWAL_REMOVEPRODUCT     Yes

Global Methods to Add, Update, and Delete Charges

Global Method: addChargeGroup (zChargeGroup)

This global method supports the following use cases:

  • Adding a new charge group from the product catalog selection of a rate plan to a new quote: When the quote is sent to Z-Billing, it will add these charges to a subscription in Z-Billing.
  • Adding a new charge group to an Amendment quote: When the quote is sent to Z-Billing, it adds a new charge group to the existing subscription as amendment.
  • Adding a new charge group to a Renewal quote: When the quote is sent to Z-Billing, it adds a new charge group to the existing subscription as an amendment of type AddProduct. Additionally, it will create an amendment of type Renewal.

In Zuora Quotes, we separate the terms, amendment and renewal. In Zuora Billing, both are amendments.

Global Method: updateChargeGroup (updateChargeGroup)

This global method supports the following use cases:

For the New Subscription type, the method supports updating an existing charge group on a New Subscription quote.

For the Amend Subscription type:

  • Updating an Original charge group on an Amend Subscription quote: Original charge groups are charges associated with the subscription that already exist in Z-Billing. This is similar to using Add Unit in the Zuora Quotes user interface.
  • Updating an AddProduct charge group on an Amend Subscription quote: Use then when editing a newly-added charge group on the quote.
  • Updating an UpdateProduct charge group on an Amendment Type quote: Use this when editing an existing original charge group that has already been updated.

For the Renewal type:

  • Updating a Renewed charge group in a Renewal quote: When a Renewal quote is created, all of the original charges from the old quote (which has a subscription in Z-Billing) are renewed and become a Renewed charge group.
  • Updating a NewProduct charge group in a Renewal quote: Use then when editing an existing new charge group on the quote.
  • Updating an UpdateProduct charge group: Use then when editing an existing renewed charge group that has already been updated.

Global Method: deleteChargeGroup (deleteChargeGroup)

This global method supports the following use cases:

For the New Subscription type, the method supports deleting a new charge group on a New Subscription quote.

For the Amendment type:

  • Deleting an Original charge group on an Amendment quote: Original charge groups are charges associated with the old quote, for which a subscription already exists in Z-Billing. This is similar to using Delete in the Zuora Quotes user interface. This results in charge group with type RemoveProduct.
  • Deleting a NewProduct charge group on an Amendment quote: Use this when deleting a added New charge group on the quote.
  • Deleting an UpdateProduct charge group on an Amendment quote: Use this when deleting an Original charge group that has been updated.
  • Deleting a RemoveProduct charge group on an Amendment quote: This is similar to using Undelete in the Zuora Quotes user interface.

For the Renewal type:

  • Deleting a Renewed charge group on a Renewal quote: When a Renewal quote is created, all of the original charges from old quote (which has a subscription in Z-Billing) are renewed and become a Renewed Charge Group. This is similar to using Delete in the Zuora Quotes user interface. This results in a charge group with the type RemoveProduct.
  • Deleting a NewProduct charge group on a Renewal quote: Use this when deleting a New charge group on the quote.
  • Deleting an UpdateProduct charge group on a Renewal quote: Use this when deleting an Original charge group that has been updated.
  • Deleting a RemoveProduct charge group on a Renewal quote: This is similar to using Undelete in the Zuora Quotes user interface.

Types and Quotes that can be Added, Updated, and Deleted

This table describes the different zChargeGroup types and quotes that can be added, updated, and deleted using the global methods.

  Global Method Supported
zChargeGroupType addChargeGroup updateChargeGroup deleteChargeGroup
TYPE_PRODUCT_NEW Yes    
TYPE_SUBSCRIPTION_ADDED   Yes Yes
TYPE_AMENDMENT_ORIGINAL   Yes Yes
TYPE_AMENDMENT_NEWPRODUCT   Yes Yes
TYPE_AMENDMENT_UPDATEPRODUCT   Yes Yes
TYPE_AMENDMENT_REMOVEPRODUCT     Yes
TYPE_RENEWAL_RENEWED   Yes Yes
TYPE_RENEWAL_NEWPRODUCT   Yes Yes
TYPE_RENEWAL_UPDATEPRODUCT   Yes Yes
TYPE_RENEWAL_REMOVEPRODUCT     Yes

Global Methods to Calculate Charges

The following global methods support the Zuora Quotes-specific calculation when the zChargeGroup is being edited.

  • calculateChargesOnDiscountChange (originalChargeList)
  • calculateChargesOnEffectivePriceChange (originalChargeList)
  • calculateChargesOnTotalChange (originalChargeList)
  • calculateChargesOnQuantityChange (originalChargeList)

You can edit only a single zCharge at a time in a zChargeGroup.

By default, these calculations are embedded in the exposed global components. Because of this, you do not have to create custom code to use the calculations.

Considerations

  • Using the getChargeGroup method, you can add only one charge group at a time to a quote.
  • Zuora recommends that you do not call the global methods in a loop, or call them one after another in a single execution of code. This will cause your code to reach the Salesforce governance limit in most situations. Design separate events to call one global method at a time.

Sample Code

=====================================================================
EditCharges.page
=====================================================================

<apex:page standardController="zqu__Quote__c"
    extensions="EditChargesController" id="quoting_page">
    <apex:form id="quoting_form">
        <apex:pageMessages id="pageErrors"/>
        <apex:pageBlock rendered="{!!initFailed}" id="quoting_page_block">
            <apex:pageBlockSection title="Selected Products"
                rendered="{!!selectNewProductViewMode}" columns="1">
                <apex:outputPanel id="charge_group_edit_panel"
                    rendered="{!editMode}">
                    <zqu:zChargeGroup editMode="{!editMode}"
                        chargeGroup="{!currentChargeGroup}">
                        <apex:actionStatus id="saveStatus">
                            <apex:facet name="start">
                                <apex:commandButton value="Saving ..." disabled="true" />
                            </apex:facet>
                            <apex:facet name="stop">
                                <apex:commandButton value="Save" action="{!saveChargeGroup}"
                                    reRender="quoting_page_block, pageErrors" status="saveStatus" />
                            </apex:facet>
                        </apex:actionStatus>
                        <apex:actionStatus id="cancelStatus">
                            <apex:facet name="start">
                                <apex:commandButton value="Canceling ..." disabled="true" />
                            </apex:facet>
                            <apex:facet name="stop">
                                <apex:commandButton value="Cancel" action="{!cancelEditing}"
                                    reRender="quoting_page_block, pageErrors" status="cancelStatus" />
                            </apex:facet>
                        </apex:actionStatus>
                    </zqu:zChargeGroup>
                </apex:outputPanel>
                <apex:outputPanel id="charge_group_detail_panel"
                    rendered="{!!editMode}">
                    <apex:repeat value="{!chargeGroupList}" var="group"
                        id="zcharge_group_list" rendered="{!chargeGroupList.size > 0}">
                        <zqu:zChargeGroup chargeGroup="{!group}" editMode="{!editMode}"
                            id="zcharge_group_block">
                            <apex:actionStatus id="editStatus">
                                <apex:facet name="start">
                                    <apex:commandButton value="Editing ..." disabled="true" />
                                </apex:facet>
                                <apex:facet name="stop">
                                    <apex:commandButton value="Edit" action="{!editChargeGroup}"
                                        rendered="{!group.ShowVisualForceEdit}"
                                        reRender="quoting_page_block, pageErrors" status="editStatus">
                                        <apex:param name="editingGroup" value="{!group.groupId}"
                                            assignTo="{!currentChargeGroupId}" />
                                    </apex:commandButton>
                                </apex:facet>
                            </apex:actionStatus>
                            <apex:actionStatus id="deleteStatus">
                                <apex:facet name="start">
                                    <apex:commandButton value="Deleting ..." disabled="true" />
                                </apex:facet>
                                <apex:facet name="stop">
                                    <apex:commandButton value="Delete"
                                        action="{!deleteChargeGroup}"
                                        rendered="{!group.ShowVisualForceDelete}"
                                        reRender="quoting_page_block, pageErrors" status="deleteStatus">
                                        <apex:param name="deleteChargeGroup" value="{!group.groupId}"
                                            assignTo="{!currentChargeGroupId}" />
                                    </apex:commandButton>
                                </apex:facet>
                            </apex:actionStatus>
                            <apex:actionStatus id="undeleteStatus">
                                <apex:facet name="start">
                                    <apex:commandButton value="Restoring ..." disabled="true" />
                                </apex:facet>
                                <apex:facet name="stop">
                                    <apex:commandButton value="UnDelete"
                                        action="{!deleteChargeGroup}"
                                        rendered="{!group.ShowVisualForceUnDelete}"
                                        reRender="quoting_page_block, pageErrors" status="undeleteStatus">
                                        <apex:param name="deletedChargeGroup" value="{!group.groupId}"
                                            assignTo="{!currentChargeGroupId}" />
                                    </apex:commandButton>
                                </apex:facet>
                            </apex:actionStatus>
                        </zqu:zChargeGroup>
                    </apex:repeat>
                </apex:outputPanel>
                <apex:actionStatus id="selectNewProductStatus">
                    <apex:facet name="start">
                        <apex:commandButton value="Querying ..." disabled="true" />
                    </apex:facet>
                    <apex:facet name="stop">
                        <apex:commandButton value="Select New Products"
                            action="{!toSelectNewProductsView}"
                            reRender="quoting_page_block, pageErrors"
                            rendered="{!! (editMode||selectNewProductViewMode) }"
                            status="selectNewProductStatus">
                        </apex:commandButton>
                    </apex:facet>
                </apex:actionStatus>
            </apex:pageBlockSection>
            <apex:outputText value="No matching product found." rendered="{!noMatchProduct}"/>
            <apex:pageBlockSection title="Select New Products"
                rendered="{!selectNewProductViewMode&&!noMatchProduct}" columns="1">
                <apex:outputPanel id="productselector_pageblock"
                    rendered="{!!editMode}">
                    <apex:outputPanel id="product_panel">
                        <zqu:zSelectTable id="product_zselect_table" title="Product Summary"
                            fieldNames="{!ProductDisplayFields}"
                            selectRowShare="{!selectedProductShare}"
                            setControllerShare="{!productDataShare}"
                            rerenderIDs="{!$Component.rateplan_panel}, {!$Component.charges_panel}, {!$Component.pageErrors}" />
                    </apex:outputPanel>

                    <apex:outputPanel id="rateplan_panel" >
                        <zqu:zSelectTable id="rateplan_zselect_table"
                            title="RatePlan Summary" fieldNames="{!ratePlanDisplayFields}"
                            selectRowShare="{!selectedRatePlanShare}"
                            setControllerShare="{!rateplanDataShare}"
                            rerenderIDs="{!$Component.charges_panel}, {!$Component.pageErrors}"
                            rendered="{!IsRenderRatePlan}"/>
                    </apex:outputPanel>

                    <apex:outputPanel id="charges_panel">
                        <zqu:zChargeGroup id="charge_zcharge_group"
                            chargeGroup="{!chargeGroup}" editMode="true"
                            rendered="{!IsRenderChargeGroup}">
                            <apex:actionStatus id="addNewProductStatus">
                                <apex:facet name="start">
                                    <apex:commandButton value="Adding ..." disabled="true" />
                                </apex:facet>
                                <apex:facet name="stop">
                                    <apex:commandButton value="Add New Product"
                                        action="{!addNewChargeGroupToQuote}"
                                        reRender="quoting_page_block, pageErrors" status="addNewProductStatus"/>
                                </apex:facet>
                            </apex:actionStatus>
                            <apex:actionStatus id="cancelNewProductStatus">
                                <apex:facet name="start">
                                    <apex:commandButton value="Canceling ..." disabled="true" />
                                </apex:facet>
                                <apex:facet name="stop">
                                    <apex:commandButton value="Cancel" action="{!cancelEditing}"
                                        reRender="quoting_page_block, pageErrors" status="cancelNewProductStatus" />
                                </apex:facet>
                            </apex:actionStatus>
                        </zqu:zChargeGroup>
                    </apex:outputPanel>
                </apex:outputPanel>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:page>

====================================================================
EditChargesController.cls
=====================================================================

public with sharing class EditChargesController {
   
    public List<zqu.zChargeGroup> chargeGroupList           {get; set;}
    public Boolean   editMode                               {get; set;}
    public String    currentChargeGroupId                   {get; set;}
    public zqu.zChargeGroup currentChargeGroup              {get; set;}
    private final zqu__Quote__c quote                       {get; set;}
    private final List<ID> productIDs;
   
    // Table data share
    public zqu.zComponentDataShare productDataShare         {get; set;}
    public zqu.zComponentDataShare rateplanDataShare        {get; set;}
    public zqu.zChargeGroup chargeGroup                     {get; set;}
    public Boolean selectNewProductViewMode                 {get; private set;}
    public Boolean initFailed                               {get; private set;}

    public final static Integer PRODUCT_TABLE_PAGE_SIZE = 2;
    public final static Integer RATEPLAN_TABLE_PAGE_SIZE = 2;
   
    public EditChargesController(ApexPages.StandardController controller) {
        initFailed = true;
        final String quoteId = controller.getId();
        if (null == quoteId || '' == quoteId) {
            appendErrorMessage('Need to specify the id of quote.');
            return;
        }  
        List<zqu__Quote__c> quotelist = [SELECT Id, Name, zqu__Opportunity__r.Name, zqu__Opportunity__r.Account.Name, zqu__Opportunity__r.Account.Id,
                                                 zqu__RecordReadOnly__c, zqu__Currency__c, zqu__Status__c, zqu__StartDate__c, RecordTypeId
                                          FROM
                                              zqu__Quote__c
                                          WHERE
                                              Id = :quoteId LIMIT 1];
       
        if(1 != quotelist.size()){
            appendErrorMessage('Invalid quote id.');
            return ;
        }
        this.quote = quotelist[0];
       
        try {        
            this.chargeGroupList = zqu.zQuoteUtil.getChargeGroups(this.quote.Id);
            this.editMode = false;
            this.selectNewProductViewMode = !(this.chargeGroupList.size() > 0);
            this.productDataShare = new zqu.zComponentDataShare();
                 
            this.productIDs = zqu.zQuoteUtil.getZProductIds(quote.zqu__Currency__c, quote.zqu__StartDate__c);        
            this.productDataShare.dataObject = zqu.ZQuoteUtil.getProductSetController(this.productIDs, this.getProductDisplayFields(), PRODUCT_TABLE_PAGE_SIZE);
        } catch(zqu.ZQException e) {
            appendErrorMessage(e.getMessage());
            return;
        }
       
        //
        //their setcontroller 
        //this.productDataShare.dataObject = yourproductsetcontoller();
        //
       
        this.rateplanDataShare = new zqu.zComponentDataShare();
        this.selectedProductShare = new zqu.zComponentDataShare();
        this.selectedRatePlanShare = new zqu.zComponentDataShare();
        this.initFailed = false;
    }
   
    public PageReference saveChargeGroup(){
        if(null != this.quote && null != this.quote.Id){
            zqu.zChargeGroup updatedChargeGroup = null;
            try {
                updatedChargeGroup = zqu.zQuoteUtil.updateChargeGroup(this.currentChargeGroup);    
            } catch (zqu.ZQException e) {
                ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR, e.getMessage());
                ApexPages.addMessage(msg);
                return null;
            }
            for(Integer i =0 ; i < this.chargeGroupList.size() ; i++){
                zqu.zChargeGroup zchargeGrp = this.chargeGroupList.get(i);
                if(updatedChargeGroup.groupId == zchargeGrp.groupId){
                    this.chargeGroupList.set(i, updatedChargeGroup);
                }
            }
        }
        this.editMode = false;
        return null;
    }

    public void cancelEditing(){
        this.editMode = false;
        this.selectNewProductViewMode = false;
    }
   
    public void editChargeGroup(){
        if(this.currentChargeGroupId != null && this.quote.Id != null){
            for(Integer i =0 ; i < this.chargeGroupList.size() ; i++){
                zqu.zChargeGroup zchargeGrp = this.chargeGroupList.get(i);
                if(this.currentChargeGroupId == zchargeGrp.groupId){
                    this.currentChargeGroup = zchargeGrp.deepclone();
                    break;
                }
            }
            this.editMode = true;
        }
    }

    public PageReference deleteChargeGroup(){
        if(this.currentChargeGroupId != null && this.quote.Id != null){
            for(Integer i =0 ; i < this.chargeGroupList.size() ; i++){
                zqu.zChargeGroup zchargeGrp = this.chargeGroupList.get(i);
                if(this.currentChargeGroupId == zchargeGrp.groupId){
                    this.currentChargeGroup = zchargeGrp;
                    zqu.zChargeGroup deletedChargeGroup = null;
                    try {
                        deletedChargeGroup = zqu.ZQuoteUtil.deleteChargeGroup(zchargeGrp);
                    } catch(zqu.ZQException e) {
                        appendErrorMessage(e.getMessage());
                        return null;
                    }
                    if(deletedChargeGroup != null){
                        this.chargeGroupList.set(i, deletedChargeGroup);
                    }else{
                        this.chargeGroupList.remove(i);
                    }
                    break;
                }
            }
        }
        return null;
    }
   
    public zqu.zComponentDataShare selectedProductShare {
        get{
            //when select product happening, get the related rateplans
            if( null != selectedProductShare ) {
                if(selectedProductShare.dataObject != null && selectedProductShare.componentActionFlag){
                    final String productId = (String) selectedProductShare.dataObject;
                   
                    //put your custom code here to retrieve the rateplan ids or the setcontroller for product rateplan
                    try {
                        List<ID> rateplanIDs = zqu.zQuoteUtil.getRatePlanIds(productId, quote.zqu__Currency__c, quote.zqu__StartDate__c);
                        this.rateplanDataShare.dataObject = zqu.zQuoteUtil.getRatePlanSetController(ratePlanIDs, getRatePlanDisplayFields(),
                        RATEPLAN_TABLE_PAGE_SIZE);
                    } catch(zqu.ZQException e) {
                        appendErrorMessage(e.getMessage());
                        return null;
                    }
                   
                    //need to disable the extra re-write to component controller here, will set to true from component action
                    selectedProductShare.componentActionFlag = false;
                    selectedRatePlanShare.dataObject = null;
                } else if (selectedProductShare.dataObject == null){
                    selectedRatePlanShare.dataObject = null;
                }
            }
            else {
                selectedProductShare = new zqu.zComponentDataShare();
            }
            return selectedProductShare;        
        }
        set;
    }
   
    public zqu.zComponentDataShare selectedRatePlanShare   {
        get{
            //when select rateplan happening, get the related chargegroup
            if(selectedRatePlanShare.dataObject != null && selectedRatePlanShare.componentActionFlag){
                final String rateplanId = (String) selectedRatePlanShare.dataObject;
                try {
                    this.chargeGroup= zqu.zQuoteUtil.getChargeGroup(quote.Id, ratePlanID);
                } catch(zqu.ZQException e) {
                    appendErrorMessage(e.getMessage());
                    return null;
                }
                //need to disable the extra re-write to component controller here, will set to true from component action
                selectedRatePlanShare.componentActionFlag = false;
            }
            return selectedRatePlanShare;
        }
        set;
    }  
   
    public PageReference addNewChargeGroupToQuote() {
        final zqu.ZChargeGroup chargeGroup;
        try {
            chargeGroup = zqu.ZQuoteUtil.addChargeGroup(this.chargeGroup);
        } catch(zqu.ZQException e) {
            ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR, e.getMessage());
            ApexPages.addMessage(msg);
            return null;
        }
        this.chargeGroupList.add(chargeGroup);
        this.selectNewProductViewMode = false;
        return null;
    }

    public void toSelectNewProductsView() {
        this.selectNewProductViewMode = true;
        this.rateplanDataShare = new zqu.zComponentDataShare();
        this.selectedProductShare = new zqu.zComponentDataShare();
        this.selectedRatePlanShare = new zqu.zComponentDataShare();
    }
   
    public String[] getProductDisplayFields() {
        return new String[] {'Id', 'Name', 'zqu__SKU__c', 'zqu__Description__c', 'zqu__ZuoraId__c'};
    }
   
    public String[] getRatePlanDisplayFields() {
        return new String[] {'Id', 'Name', 'zqu__Description__c', 'zqu__ZuoraId__c', 'zqu__EffectiveStartDate__c', 'zqu__EffectiveEndDate__c', 'zqu__ZProduct__c'};
    }
   
    public Boolean getIsRenderRatePlan(){
        return this.selectedProductShare.dataObject != null;
    }
   
    public Boolean getIsRenderChargeGroup(){
        return this.selectedRatePlanShare.dataObject != null;
    }

    public Boolean getNoMatchProduct() {
        return (null == this.productIDs || 0 == this.productIDs.size());
    }
   
    private static void appendErrorMessage(String message) {
        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, message));
    }    
}