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:
- zCharge: The zCharge global class represents a single charge in Zuora Quotes. It can be new, saved, or amended.
- zChargeGroup: The 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)); } }