function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Merry SMerry S 

​How to reference an ID within a record that has not been saved, in visualforce extension

I have modified an extention controller that was originally set up to add products from a pricebook to opportunities. I changed it so that it would add our product (mc product) to an account. mc product is related to both the account and the product. 

Here is what the page looks like:
User-added image

When I click "Select" I get this error - invalid ID field: null
Error is in expression '{!addToShoppingCart}' in component <apex:commandButton> in page accountproductentry: Class.accountProductEntryExtension.updateAvailableList: line 53, column 1
Class.accountProductEntryExtension.addToShoppingCart: line 79, column 1


Here are the snippets of code:
public void updateAvailableList() {
    
        // We dynamically build a query string and exclude items already in the shopping cart
        String qString = 'Select MC_Product_Record_Type__c, Name, License_Key__c, IsActive, Includes_Other_Products__c, Id, Family, Description From Product2 where IsActive=true';
        // note that we are looking for the search string entered by the user in the name OR description
        // modify this to search other fields if desired
        if(searchString!=null){
            qString+= ' and (Product2.Name like \'%' + searchString + '%\' or Product2.Description like \'%' + searchString + '%\')';
        }
        
        Set<Id> selectedEntries = new Set<Id>();
        for(MC_Products__c d:shoppingCart){
            selectedEntries.add(d.Product__r.Id);
        }
        
        if(selectedEntries.size()>0){
            String tempFilter = ' and Id not in (';
            for(Id i : selectedEntries){
                tempFilter+= '\'' + (String)i + '\',';
            }
            String extraFilter = tempFilter.substring(0,tempFilter.length()-1);
            extraFilter+= ')';
            
            qString+= extraFilter;
        }
        
        qString+= ' order by Product2.Name';
        qString+= ' limit 101';
        
        system.debug('qString:' +qString);        
        AvailableProducts = database.query(qString); LINE 53
        
        // We only display up to 100 results... if there are more than we let the user know (see vf page)
        if(AvailableProducts.size()==101){
            AvailableProducts.remove(100);
            overLimit = true;
        }
        else{
            overLimit=false;
        }
    }
    
    public void addToShoppingCart(){
    
        // This function runs when a user hits "select" button next to a product
    
        for(Product2 d : AvailableProducts){
        String mcpName = d.name;
            if((String)d.Id==toSelect && d.MC_Product_Record_Type__c.equals('MC Product - Solution Package')){
                shoppingCart.add(new MC_Products__c(Account__c=theAcc.Id, Product__c=d.Id, Name=mcpName,Product_Status__c = 'Purchased', RecordTypeId = '01230000000rTGX'));
                break;
  
  
            }
        }

        updateAvailableList();   LINE 79
    }
So - here are my thoughts on what is happening. When I select a product, it tried to add the MC Product to the "shoppingcart' and since it has not been saved and there is no product id, the update of the available list fails. I can see in the debug log that it returns a null id.
 
23:18:26.078 (78141130)|USER_DEBUG|[51]|DEBUG|qString:Select MC_Product_Record_Type__c, Name, License_Key__c, IsActive, Includes_Other_Products__c, Id, Family, Description From Product2 where IsActive=true and (Product2.Name like '%acc%' or Product2.Description like '%acc%') and Id not in ('01ta0000005cZQNAA2','01t30000001uWzWAAU','01t30000001uWzUAAU','01t30000001uWyaAAE','01t30000001uWybAAE','01t30000001uWy7AAE','01t30000001uWzXAAU','01t30000001uWy6AAE','01t30000001uWy5AAE','null') order by Product2.Name limit 101
23:18:26.078 (78710544)|EXCEPTION_THROWN|[52]|System.QueryException: invalid ID field: null

My question is what can I do to fix this. I tried inserting the record first, which never worked and not really how I want it to work. I have the id populating the right field for the product, but it appears that it does not work, or since the reocrd have not been saved it cannot refernce it.

You will see in my code I am being really only set up to click one type of product, I wanted to get that working before adding more. There is also some code that I don't need, but I have not been worried about that just yet. 

Extension:
public with sharing class accountProductEntryExtension {

    public Account theAcc {get;set;}
    public String searchString {get;set;}
    public list<MC_Products__c> shoppingCart {get;set;}
    public list<MC_Products__c> mcpToAdd {get;set;}
    public list<Product2> AvailableProducts { get; set; }
    public MC_Products__c theProd {get;set;}   
    
    public String toSelect {get; set;}
    public String toUnselect {get; set;}
    public Decimal Total {get;set;}
    public Boolean overLimit {get;set;}
    
    private MC_Products__c[] forDeletion = new MC_Products__c[]{};

    public accountProductEntryExtension(ApexPages.StandardController controller) {
    	   theAcc = [select Id from Account where Id = :controller.getRecord().Id limit 1];

       shoppingCart = [Select Product__r.Name, Account__r.Name, Product__r.Id, Name, Account__c, View_Only_Licenses__c, Product__c, RecordTypeId, Product_Status__c, MC_Product_Notes__c, License_Key__c, Integration__c, Full_Licenses__c From MC_Products__c where Account__c=:theAcc.Id];
    }

    public void updateAvailableList() {
    
        // We dynamically build a query string and exclude items already in the shopping cart
        String qString = 'Select MC_Product_Record_Type__c, Name, License_Key__c, IsActive, Includes_Other_Products__c, Id, Family, Description From Product2 where IsActive=true';
        // note that we are looking for the search string entered by the user in the name OR description
        // modify this to search other fields if desired
        if(searchString!=null){
            qString+= ' and (Product2.Name like \'%' + searchString + '%\' or Product2.Description like \'%' + searchString + '%\')';
        }
        
        Set<Id> selectedEntries = new Set<Id>();
        for(MC_Products__c d:shoppingCart){
            selectedEntries.add(d.Product__r.Id);
        }
        
        if(selectedEntries.size()>0){
            String tempFilter = ' and Id not in (';
            for(Id i : selectedEntries){
                tempFilter+= '\'' + (String)i + '\',';
            }
            String extraFilter = tempFilter.substring(0,tempFilter.length()-1);
            extraFilter+= ')';
            
            qString+= extraFilter;
        }
        
        qString+= ' order by Product2.Name';
        qString+= ' limit 101';
        
        system.debug('qString:' +qString);        
        AvailableProducts = database.query(qString);
        
        // We only display up to 100 results... if there are more than we let the user know (see vf page)
        if(AvailableProducts.size()==101){
            AvailableProducts.remove(100);
            overLimit = true;
        }
        else{
            overLimit=false;
        }
    }
    
    public void addToShoppingCart(){
    
        // This function runs when a user hits "select" button next to a product
    
        for(Product2 d : AvailableProducts){
        String mcpName = d.name;
            if((String)d.Id==toSelect && d.MC_Product_Record_Type__c.equals('MC Product - Solution Package')){
                shoppingCart.add(new MC_Products__c(Account__c=theAcc.Id, Product__c=d.Id, Name=mcpName,Product_Status__c = 'Purchased', RecordTypeId = '01230000000rTGX'));
                break;
  
  
            }
        }

        updateAvailableList();  
    }
    

    public PageReference removeFromShoppingCart(){
    
        // This function runs when a user hits "remove" on an item in the "Selected Products" section
    
        Integer count = 0;
    
        for(MC_Products__c d : shoppingCart){
            if((String)d.Account__c==toUnselect){
            
                if(d.Id!=null)
                    forDeletion.add(d);
            
                shoppingCart.remove(count);
                break;
            }
            count++;
        }
        
        updateAvailableList();
        
        return null;
    }
    
    public PageReference onSave(){
    
        // If previously selected products are now removed, we need to delete them
        if(forDeletion.size()>0)
            delete(forDeletion);
    
        // Previously selected products may have new quantities and amounts, and we may have new products listed, so we use upsert here
        try{
            if(shoppingCart.size()>0)
                upsert(shoppingCart);
        }
        catch(Exception e){
            ApexPages.addMessages(e);
            return null;
        }  
           
        // After save return the user to the Opportunity
        return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id'));
    }
    
    public PageReference onCancel(){
 
        // If user hits cancel we commit no changes and return them to the Opportunity   
        return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id'));
    }


}

VF Page
<apex:page standardController="Account" extensions="accountProductEntryExtension" >

    <apex:sectionHeader Title="Manage {!$ObjectType.Product2.LabelPlural}" subtitle="{!account.Name}"/>
    <apex:messages style="color:red"/>

    <style>
        .search{
            font-size:14pt;
            margin-right: 20px;    
        }
        .fyi{
            color:red;
            font-style:italic;
        }
        .label{
            margin-right:10px;
            font-weight:bold;
        }
    </style>
    
    <script type='text/javascript'>
    
        // This script assists the search bar functionality
        // It will execute a search only after the user has stopped typing for more than 1 second
        // To raise the time between when the user stops typing and the search, edit the following variable:
        
        var waitTime = 1;
        
    
        var countDown = waitTime+1;
        var started = false;
        
        function resetTimer(){
        
            countDown=waitTime+1;
            
            if(started==false){
                started=true;
                runCountDown();
            }
        }
        
        function runCountDown(){
        
            countDown--;
            
            if(countDown<=0){
                fetchResults();
                started=false;
            }
            else{
                window.setTimeout(runCountDown,1000);
            }
        }
    
    </script>
   
  
    <apex:form >

            <br/>
            
<!-- this is the upper table... a.k.a. the "Shopping Cart"-->

            <!-- notice we use a lot of $ObjectType merge fields... I did that because if you have changed the labels of fields or objects it will reflect your own lingo -->
            <apex:pageBlock title="Selected {!$ObjectType.Product2.LabelPlural}" id="selected">
                       
                <apex:pageblockTable value="{!shoppingCart}" var="s">
                

                    <apex:column headerValue="{!$ObjectType.MC_Products__c.Fields.Account__c.Label}" value="{!s.Account__r.Name}"/>
                    
                    <apex:column headerValue="{!$ObjectType.MC_Products__c.LabelPlural}" value="{!s.Name}"/>

                    <apex:column headerValue="{!$ObjectType.MC_Products__c.Fields.Product__c.Label}" value="{!s.Product__r.Name}"/>

                    <apex:column headerValue="{!$ObjectType.MC_Products__c.Fields.Product_Status__c.Label}">
                        <apex:inputField value="{!s.Product_Status__c}" style="width:70px" required="true"/>
                    </apex:column>

                    <apex:column headerValue="{!$ObjectType.MC_Products__c.Fields.License_Key__c.Label}">
                        <apex:inputField value="{!s.License_Key__c}" style="width:70px"/>
                    </apex:column>

                    <apex:column headerValue="{!$ObjectType.MC_Products__c.Fields.Integration__c.Label}">
                        <apex:inputField value="{!s.Integration__c}" style="width:70px" required="false"/>
                    </apex:column>

                    <apex:column headerValue="{!$ObjectType.MC_Products__c.Fields.View_Only_Licenses__c.Label}">
                        <apex:inputField value="{!s.View_Only_Licenses__c}" style="width:70px" required="false"/>
                    </apex:column>

                    <apex:column headerValue="{!$ObjectType.MC_Products__c.Fields.Full_Licenses__c.Label}">
                        <apex:inputField value="{!s.Full_Licenses__c}" style="width:70px" required="false"/>
                    </apex:column>
                                                           
                    <apex:column headerValue="{!$ObjectType.MC_Products__c.Fields.MC_Product_Notes__c.Label}">
                        <apex:inputField value="{!s.MC_Product_Notes__c}" required="false"/>
                    </apex:column>
                    
                </apex:pageblockTable>
            
            
                <apex:pageBlockButtons >
                    <apex:commandButton action="{!onSave}" value="Save"/>
                    <apex:commandButton action="{!onCancel}" value="Cancel" immediate="true"/>
                </apex:pageBlockButtons>
            
            </apex:pageBlock>
    
<!-- this is the lower table: search bar and search results -->
    
            <apex:pageBlock >
            
                <apex:outputPanel styleClass="search">
                    Search for {!$ObjectType.Product2.LabelPlural}:
                </apex:outputPanel>

                <apex:actionRegion renderRegionOnly="false" immediate="true">
                
                    <apex:actionFunction name="fetchResults" action="{!updateAvailableList}" reRender="searchResults" status="searchStatus"/>
                    
                    <!-- here we invoke the scripting to get out fancy 'no button' search bar to work -->
                    <apex:inputText value="{!searchString}" onkeydown="if(event.keyCode==13){this.blur();}else{resetTimer();}" style="width:300px"/>
                    &nbsp;&nbsp;
                    <i>
                        <!-- actionStatus component makes it easy to let the user know when a search is underway -->
                        <apex:actionStatus id="searchStatus" startText="searching..." stopText=" "/>
                    </i>
                    
                </apex:actionRegion>
            
                <br/>
                <br/>
            
                <apex:outputPanel id="searchResults">
                
                    <apex:pageBlockTable value="{!AvailableProducts}" var="a">
                    
                        <apex:column headerValue="{!$ObjectType.Product2.Fields.Name.Label}" value="{!a.Name}" />
                        
                        <apex:column headerValue="{!$ObjectType.Product2.Fields.Family.Label}" value="{!a.Family}"/>
                        
                        <apex:column headerValue="{!$ObjectType.Product2.Fields.Description.Label}" value="{!a.Description}"/>
                        
                        <apex:column >
                            <!-- command button in a column... neato -->
                            <apex:commandButton value="Select" action="{!addToShoppingCart}" reRender="selected,searchResults" immediate="true">
                                <!-- again we use apex:param to be able to tell the controller which row we are working with -->
                                <apex:param value="{!a.Id}" assignTo="{!toSelect}" name="toSelect"/>
                            </apex:commandButton>
                        </apex:column>
                        
                    </apex:pageBlockTable>
                    
                    <!-- We put up a warning if results exceed 100 rows -->
                    <apex:outputPanel styleClass="fyi" rendered="{!overLimit}">
                        <br/>
                        Your search returned over 100 results, use a more specific search string if you do not see the desired {!$ObjectType.Product2.Label}.
                        <br/>
                    </apex:outputPanel>
                    
                </apex:outputPanel>
            
            </apex:pageBlock>
            


    </apex:form>

</apex:page>



 
Santanu HalderSantanu Halder
Change your this line
selectedEntries.add(d.Product__r.Id);

to this
selectedEntries.add(d.Product__c);