Happy Business Starts Here

Senior Tutor

Bill to and Sold to Contacts outside of Opportunity's Account

I have contacts that live outside the accounts that may be the bill to and sold to contacts for a quote/opportunitiy.  In Guided Selling, the Bill to and Sold To lookups provide only the account where the Opportunity resides.  I'd like to be able to open up this functionality and allow the user to select any contact within the account's hierarhcy.  


I have realized that they can change the Bill to and Sold to on the Quote after they completed the intial Guided Selling steps.  This is less than ideal.  Any ideas how to allow the end user to select any contact when in the middle of guided selling? 

Zuora Alumni

Re: Bill to and Sold to Contacts outside of Opportunity's Account

There is a setting deep within Quotes that might help, but not sure if it fits your use case:



That setting in the Zuora Config tab will show you Contacts from the immediate parent billing account or the child billing account (depending on where the account is in the hierarchy).  It does this by knowing how the billing accounts link to the Salesforce Accounts, and then shows you those contacts from the Salesforce Accounts.


Again, doesnt show ANY contact, but might help.

Senior Tutor

Re: Bill to and Sold to Contacts outside of Opportunity's Account

I have this setting enabled but I'm not sure that our API Sandbox has accurate data with respect to Billing Accounts and definitely doesnt have all the subscription information.


Is there a way to refresh the Zuora API Sandbox with Production data? Since our Full Copy Sandbox for Salesforce has all the records but they don't line up with what is in API Sandbox for Zuora

Zuora Alumni

Re: Bill to and Sold to Contacts outside of Opportunity's Account

Hi @wkoscielny , To answer your specific question if Zuora API snadbox could be refreshed with Production data, unfortunately the answer is No. If you have Zuora Services Tenant, that can be refreshed with Zuora production tenant Data .


Also, regarding Exposing BillToContact and SoldToContact to look up All contacts in entire account hierarchy, I know Zuora Global services has done this for other customers. In high level, they were  able to achieve it using the DefaultValues plugin on the CreateQuote component. In this plugin you can access the LookupComponentOptions for the bill to / sold to fields, and change the filter yourself.


Thank you


If you found my answer helpful, please give me a kudo ↑
Help others find answers faster by accepting my post as a solution √

Zuora Staff

Re: Bill to and Sold to Contacts outside of Opportunity's Account

This is possible by using default value plugin and overriding the lookup component.Please find the code below


global class DefaultValues extends zqu.CreateQuoteController.PopulateDefaultFieldValuePlugin{ 

    //Helpers for Quote Type attributes
    private static final String QT_SUB   = 'Subscription';
    private static final String QT_AMEND = 'Amendment';
    private static final String QT_RENEW = 'Renewal';

    //Helpers for URL Params
    private static final String URL_QUOTETYPE = 'quoteType';
    private static final String URL_QUOTEID   = 'quoteId';
    private static final String URL_SUBID     = 'subscriptionId';

    global override void populateDefaultFieldValue(SObject record, zqu.PropertyComponentController.ParentController pcc){     
        final String FUNC_STR = DEBUG_STR+'[populateDefaultFieldValue] ';

        //Leverage OOTB defaulting
        if(!Test.isRunningTest()) {
            super.populateDefaultFieldValue(record, pcc);    

        Id accountId = (Id) record.get('zqu__Account__c'); 
        Id opportunityId = (Id) record.get('zqu__Opportunity__c');
        System.debug(FUNC_STR+' opportunityId '+opportunityId);
        zqu.LookupComponentOptions billToOptions;
        zqu.LookupComponentOptions soldToOptions;

        //if the name is longer than 80 characters, we trim it
        String quoteName = String.valueOf(record.get('Name'));
        if(String.isNotBlank(quoteName) && quoteName.length()>80){
        //if the amendment name is longer than 80 characters, we trim it
        String amendmentName = String.valueOf(record.get('zqu__Amendment_Name__c'));
        if(String.isNotBlank(amendmentName) && amendmentName.length()>80){
            record.put('zqu__Amendment_Name__c', amendmentName.left(80));
        String quoteType = ApexPages.currentPage().getParameters().get(URL_QUOTETYPE);
        quoteType = (String.isNotBlank(quoteType)) ? quoteType : QT_SUB; 

        Zuora__Subscription__c sub;
        zqu__quote__c quote;
       // perform org dependent functionality
        performOrgSpecificOverrides(record, pcc);
        record.put('zqu__InvoiceSeparately__c', true);
            record.put('zqu__BillingBatch__c', 'Batch1');
            record.put('zqu__Subscription_Term_Type__c', 'Termed');
            record.put('zqu__PaymentTerm__c','Due Upon Receipt');
            record.put('zqu__PaymentMethod__c', 'Check');
            record.put('zqu__InitialTerm__c', 12);   
            record.put('zqu__RenewalTerm__c', 12);   
            record.put('zqu__InitialTermPeriodType__c', 'Month');
            record.put('zqu__RenewalTermPeriodType__c', 'Month');
            record.put('zqu__QuoteBusinessType__c', 'New');

            record.put('zqu__QuoteBusinessType__c', 'Upsell'); 

            record.put('zqu__QuoteBusinessType__c', 'Renewal');  
        List<Id> acctList = new List<Id>();
        String soqlFragment = buildSqlFragment(acctList);

        // Before retrieving  the lookup  options, needs to populate the map first  
            // Now retrieve the lookup component options            
            billToOptions = super.getLookupOption('zqu__BillToContact__c');   
            soldToOptions = super.getLookupOption('zqu__SoldToContact__c');  
            billToOptions.lookupComponentControllerName = 'Z_LookupComponentController'; 
            soldToOptions.lookupComponentControllerName =  'Z_LookupComponentController';  

            billToOptions = new zqu.LookupComponentOptions(); 
            soldToOptions = new zqu.LookupComponentOptions(); 


        System.debug(FUNC_STR+'Bill To Contact options: '+super.getLookupOption('zqu__BillToContact__c'));
        System.debug(FUNC_STR+'Sold To Contact options: '+super.getLookupOption('zqu__SoldToContact__c'));

   public String buildSqlFragment(List<Id> acctList){

        String acctIdSoqlFragment = 'accountId = ';

        //build the soql fragment in the form ('a4fa0000000KzbV','a4fa0000000KzbW')
        for(integer i = 0; i < acctList.size(); i++){
            if (i == acctList.size()-1) {
                acctIdSoqlFragment += '\''+ acctList.get(i) +'\''; 
                acctIdSoqlFragment += '\''+ acctList.get(i) +'\' OR accountId = '; 

        return acctIdSoqlFragment; 
    private void performOrgSpecificOverrides(SObject record, zqu.PropertyComponentController.ParentController pcc){

Lookup Component 

global class Z_LookupComponentController extends zqu.LookupComponentController{
  public zqu.LookupComponentOptions optionsForLookup {get;set;}
   private static final String MASTER_RECORDTYPEID = Contact.SObjectType.getDescribe().getRecordTypeInfosByName().get('Master').getRecordTypeId();
   public static zqu.LookupComponentOptions createContactLookupComponentOptions(Id oppId, Id accId, Contact soldToContact) {
        zqu.LookupComponentOptions optionsForShipTo = new zqu.LookupComponentOptions();
        optionsForShipTo.objectName = 'Contact';
        optionsForShipTo.Id = 'soldToContact';
        optionsForShipTo.isEditMode = true;
        optionsForShipTo.isRequired = true;
        optionsForShipTo.lookupComponentControllerName = 'Z_LookupComponentController';
        optionsForShipTo.recordTypeId = MASTER_RECORDTYPEID;
        optionsForShipTo.popupWindowTitle = 'Sold To Contact Lookup';
        optionsForShipTo.soqlFragment = buildSoqlFragment(accId);

        if (soldToContact != null) {
            optionsForShipTo.targetId = soldToContact.Id;
            optionsForShipTo.targetName = soldToContact.Name;

        return optionsForShipTo;
  public static String buildSoqlFragment (Id accountId){
        List<id> accList = new List<id>();
        String accIdSoqlFragment;        
        //build the soql fragment in the form ('a4fa0000000KzbV','a4fa0000000KzbW')
        for(integer i = 0; i < accList.size(); i++){
            if (i == accList.size()-1) {
                accIdSoqlFragment += '\''+ accList.get(i) +'\''; 
                accIdSoqlFragment += '\''+ accList.get(i) +'\''; 

        return accIdSoqlFragment;


Once the default values plugin Class is registered in Component registration we will be able to see all the contacts from salesforce once lookup icon is clicked in the bill to and sold to contacts