Create the Billing Account and Subscription Selection User Interface
This article presents a sample code for creating a custom page used for selecting a billing account and a subscription in Zuora Quotes 5.92 or earlier versions.
In the later versions of Zuora Quotes, you can use the SelectBillingAccount component to create the custom page.
Sample code description
PrepareNewQuote Visualforce page and PrepareNewQuoteController class:
- Called from custom Create Quote button from the Opportunity detail page.
- Captures the Opportunity-related, Account-related, and Contact-related data and uses the Order Builder code to check for the existence of a Z-Billing account and related subscriptions.
- Provides a Z-Billing Account and/or Subscription selection user interface to create the following types of Quotes:
- New Subscription Quote: RecordType = Default, Subscription Type = New Subscription
- Amend Subscription Quote: RecordType = Amend, Subscription Type = Amend Subscription
- Renew Subscription Quote: RecordType = Renew, Subscription Type = Renew Subscription
- Pass the following information to the NewQuote visual force page as URL parameters:
- Opportunity Record ID: Salesforce opportunity record ID, from where the Create Quote button is clicked.
- Billing Customer Account ID: ID of Z-Billing customer account. To create a new customer account, provide "New" or any other indicator for creating a new account.
- Billing Subscription ID: The ID of a Z-Billing subscription. Provide "New" or any other indicator for creating a new subscription.
- Quote Type: The Salesforce Quote’s "Subscription Type."
Sample code
PrepareNewQuote.page <apex:page showHeader="true" sidebar="true" standardController="Opportunity" extensions="PrepareNewQuoteController" action="{!onload}"> <apex:form > <apex:pageMessages id="pageErrors"/> <apex:pageBlock rendered="{!!initFailed}"> <apex:pageBlockSection columns="1" id="accountsection"> <apex:outputLabel rendered="{!billingAccountList.size != 0}" value="There is one or more Z-Billing Account corresponding to this Salesforce Account." /> <apex:outputLabel value="Please select the account to use:" style="font-weight:bold"></apex:outputLabel> <apex:selectRadio layout="pageDirection" id="accTypeRadio" value="{!selectedAccountType}"> <apex:actionSupport event="onclick" action="{!onSelectAccountType}" reRender="accListRadio, subscriptionSection, pageErrors"/> <apex:selectOption itemEscaped="false" itemlabel="New billing account" itemValue="new" id="new_billingaccount"/> <apex:selectOption itemEscaped="false" itemLabel="Existing billing account" itemValue="existing" itemDisabled="{!billingAccountList.size == 0}" id="existing_billingaccount"></apex:selectOption> </apex:selectRadio> <apex:outputPanel id="accListRadio"> <apex:selectRadio layout="pageDirection" value="{!selectedAccount}" style="position:relative;left:5%" rendered="{!selectedAccountType == 'existing' && billingAccountList.size > 0 }"> <apex:actionSupport event="onclick" action="{!onSelectAccount}" reRender="subscriptionSection, pageErrors"/> <apex:selectOptions value="{!billingAccountList}" ></apex:selectOptions> </apex:selectRadio> </apex:outputPanel> </apex:pageBlockSection> <apex:pageBlockSection columns="1" id="subscriptionSection"> <apex:outputLabel value="Create quote for:" style="font-weight:bold"></apex:outputLabel> <apex:selectRadio layout="pageDirection" id="subTypeRadio" value="{!selectedSubscriptionType}" > <apex:actionSupport event="onclick" action="{!onSelectSubscriptionType}" reRender="existingSubscriptionOptions, pageErrors"/> <apex:selectOption itemEscaped="false" itemLabel="New subscription for this account" itemValue="new"> </apex:selectOption> <apex:selectOption itemEscaped="false" itemLabel="Existing subscription" itemValue="existing" itemDisabled="{!subscriptionList.size == 0}" rendered="{!selectedAccountType == 'existing'}"></apex:selectOption> </apex:selectRadio> <apex:outputPanel id="existingSubscriptionOptions"> <apex:selectList value="{!selectedSubscription}" style="position:relative;left:3%;background:#fbfbd7" size="1" rendered="{!selectedSubscriptionType == 'existing' && subscriptionList.size > 0}"> <apex:selectOptions value="{!subscriptionList}"></apex:selectOptions> </apex:selectList> <apex:outputPanel style="position:relative;left:5%" rendered="{!selectedSubscriptionType == 'existing' && subscriptionList.size > 0}"> <apex:selectRadio style="position:relative;left:5%" layout="pageDirection" value="{!selectedAmendmentType}"> <apex:selectOption itemescaped="false" itemLabel="Amend this subscription" itemValue="amend"></apex:selectOption> <apex:selectOption itemescaped="false" itemLabel="Renew this subscription" itemValue="renew" ></apex:selectOption> </apex:selectRadio> </apex:outputPanel> </apex:outputPanel> </apex:pageBlockSection> <apex:pageBlockButtons location="bottom"> <apex:commandButton value="Continue" action="{!goNewQuote}"/> <apex:commandButton value="Cancel" action="{!cancel}"/> </apex:pageBlockButtons> </apex:pageBlock> </apex:form> </apex:page> ==================================================================== PrepareNewQuoteController.cls public with sharing class PrepareNewQuoteController{ private Zuora.zApi api = new Zuora.zApi(); private final Opportunity opp; private final String CRMId; private final Map <String,List<SelectOption>> cachedSubOptions = new Map <String,List<SelectOption>>(); public Boolean initFailed {get; private set;} public String selectedAccountType {get;set;} public String selectedAccount {get;set;} public String selectedSubscription {get;set;} public String selectedSubscriptionType {get;set;} public String selectedAmendmentType {get;set;} public List<SelectOption> billingAccountList {get;set;} public List<SelectOption> subscriptionList {get;set;} public PrepareNewQuoteController(ApexPages.StandardController controller) { this.initFailed = true; final String oppId = controller.getId(); if (null == oppId || '' == oppId) { appendErrorMessage('Need to specify the id of opportunity to create a quote.'); return; } final List<Opportunity> oppList = [SELECT Id, Account.Id FROM Opportunity WHERE Id = :oppId limit 1]; if (oppList.size() != 1) { appendErrorMessage('Invalid opportunity specified to create a quote.'); return; } this.opp = oppList[0]; this.CRMId = this.opp.Account.Id; try { api.zlogin(); } catch (Zuora.zAPIException e) { appendErrorMessage(e.getMessage()); return; } this.initFailed = false; } public PageReference onload() { try { this.loadBillingAccountList(); } catch (Zuora.zAPIException e) { appendErrorMessage(e.getMessage()); return null; } return null; } public PageReference onSelectAccount() { try { this.loadSubscriptionList(); } catch (Zuora.zAPIException e) { appendErrorMessage(e.getMessage()); return null; } return null; } public PageReference goNewQuote() { PageReference newQuotePage = Page.NewQuote; String quoteType = 'New'; if ('amend' == selectedAmendmentType ) { quoteType = 'Amend'; } else if ('renew' == selectedAmendmentType) { quoteType = 'Renew'; } newQuotePage.getParameters().put('quoteType', quoteType); newQuotePage.getParameters().put('billingaccountid', this.selectedAccount); newQuotePage.getParameters().put('existsubscriptionid', this.selectedSubscription); newQuotePage.getParameters().put('oppid', String.valueOf(this.opp.Id).substring(0,15)); newQuotePage.setRedirect(true); return newQuotePage; } public PageReference onSelectAccountType() { if ('new' == this.selectedAccountType) { this.selectedSubscriptionType = 'new'; } else { try { this.loadBillingAccountList(); } catch (Zuora.zAPIException e) { appendErrorMessage(e.getMessage()); return null; } this.selectDefaultAccount(); } return null; } private void selectDefaultAccount() { if (this.billingAccountList != null && this.billingAccountList.size() > 0) { this.selectedAccount = this.billingAccountList[0].getValue(); } this.onSelectAccount(); } public PageReference onSelectSubscriptionType() { if ('existing' == this.selectedSubscriptionType) { try { this.loadSubscriptionList(); } catch (Zuora.zAPIException e) { appendErrorMessage(e.getMessage()); return null; } this.selectDefaultSubscription(); } return null; } private void selectDefaultSubscription() { if (this.subscriptionList.size() > 0) { this.selectedAmendmentType = 'amend'; this.selectedSubscription = this.subscriptionList[0].getValue(); } } private void loadBillingAccountList() { if (this.CRMId == null) return; //already queried from Zuora, no need to query again if (this.billingAccountList != null) return; else{ //query from Zuora and build the billing account picklist this.billingAccountList = new List<SelectOption> (); String acczoql = 'SELECT AccountNumber, Name FROM Account WHERE Status = \'Active\' AND CrmId LIKE \'' + this.CRMId.substring(0, 15) + '%\''; List <Zuora.zObject> acclist; try { acclist = api.zquery(acczoql); } catch (Zuora.zAPIException e) { throw e; } for(Zuora.zObject acc: acclist) { String accvalue = (String)acc.getValue('Id'); String acclabel = acc.getValue('Name') + ', Acct#:' + acc.getValue('AccountNumber'); SelectOption accso = new SelectOption (accvalue, acclabel); this.billingAccountList.add(accso); } } } //query the subscription from zuora and build the picklist private void loadSubscriptionList() { this.subscriptionList = new List<SelectOption>(); if (this.selectedAccount == null) return; List<SelectOption> suboptionlist = this.cachedSubOptions.get(this.selectedAccount); if (suboptionlist != null) this.subscriptionList = suboptionlist; else{ suboptionlist = new List<SelectOption> (); String subzoql = 'SELECT Id, Name, ContractEffectiveDate, TermType, Status FROM Subscription WHERE TermType= \'TERMED\' AND Status = \'Active\' AND AccountId = \'' + this.selectedAccount + '\''; List<Zuora.zObject> subscriptionList; try { subscriptionList = api.zquery(subzoql); } catch (Zuora.zAPIException e) { throw e; } for(Zuora.zObject sub : subscriptionList) { String subvalue = (String)sub.getValue('Id'); DateTime cedatetime = (DateTime)sub.getValue('ContractEffectiveDate'); String sublabel = (String)sub.getValue('Name') + ', Status: ' + (String)sub.getValue('Status') + ', Contract Effective Date: ' + cedatetime.date().format(); SelectOption subso = new SelectOption (subvalue,sublabel); suboptionlist.add(subso); } if (suboptionlist.size() > 0){ this.cachedSubOptions.put(this.selectedAccount, suboptionlist); this.subscriptionList = suboptionlist; } } } static private void appendErrorMessage(String message) { ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, message)); } }
Considerations
- Order Builder code should be used to get the customer account information from Z-Billing. See Order Builder for more information.
- The login information should be properly put into the Order Builder configuration and the Z-Billing connection should be configured.
- The parameter name can be different from the one that is specified in the sample page and controller, but the values must be the same.