Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

I am creating a new opportunity wizard to ensure that specific data is always entered when our sales reps create a new opportunity. As part of this effort, I've also over-written the lookup window for the Account lookup (leveraging this awesome post: Roll Your Own Salesforce "Lookup" Popup Window). On the second page of my wizard, after the user has selected an Account, I have them select the contacts and contact roles that will be associated with the opportunity. I want to over-write the lookup window for Contacts as well and limit the contacts displayed to only contacts that are associated with the Account the user selected in step one of the wizard.

My approach was to create a class that would be an extension for the Contact Lookup page and the controller for this page would be my custom controller that I'm using for the new opportunity wizard. My thinking here was that I need to be able to get access to the Account ID that the user selected in step one, so that I can use that to filter the contacts that I display in the contact lookup page.

However, I must not be understanding something basic here because I can't seem to get access to the custom controller's properties.

My assumption is that when the CustomContactLookupController is called that the createNewOpportunity controller is calling the constructor for CustomContactLookupController and passing a reference to itself? So, I should be able to access the properties of the controller class (e.g. I have a property "tempAcc" that I use to store the Account ID of the account the user selected in step one of the opportunity wizard).

However, when I look at the debug log it's showing that values are "null" in the constructor method for my CustomContactLookupController when I would expect them not to be? It's like it's creating a new instance of createNewOpportunity and passing that to the constructor?

Here's my controller class:

public with sharing class createNewOpportunity {

Public Opportunity opportunity {get; set;}
Public Account tempAcc {get; set;}
Private Final Id currentUserId {get; set;}
Public List<Opportunity> openOpps {get; set;}

Public Contact contact1 {get; set;}
Public Contact contact2 {get; set;}
Public Contact contact3 {get; set;}
Public Contact contact4 {get; set;}
Public Contact contact5 {get; set;}

Public OpportunityContactRole role1 {get; set;}
Public OpportunityContactRole role2 {get; set;}
Public OpportunityContactRole role3 {get; set;}
Public OpportunityContactRole role4 {get; set;}
Public OpportunityContactRole role5 {get; set;}


public createNewOpportunity()
{
    opportunity = new Opportunity();
    opportunity.StageName = 'Buyer Needs';      
    opportunity.OwnerId = UserInfo.getUserId();
    opportunity.ForecastCategoryName = 'Omitted';
    opportunity.Probability = 0;

    tempAcc = new Account();
    currentUserId = UserInfo.getUserId();

    role1 = new OpportunityContactRole();
    role1.opportunityId = opportunity.id; 

    role2 = new OpportunityContactRole();
    role2.opportunityId = opportunity.id;

    role3 = new OpportunityContactRole();
    role3.opportunityId = opportunity.id;

    role4 = new OpportunityContactRole();
    role4.opportunityId = opportunity.id;

    role5 = new OpportunityContactRole();
    role5.opportunityId = opportunity.id;

    System.debug('In Constructor for createNewOpportunity, tempAcc.Id: ' + tempAcc.Id);
    System.debug('In Constructor for createNewOpportunity, opportunity.AccountId: ' + opportunity.AccountId);
}

public PageReference step1()
{
    System.debug('In step1, tempAcc.Id: ' + tempAcc.Id);
    System.debug('In step1, opportunity.AccountId: ' + opportunity.AccountId);
    return Page.newOpportunityStep1;
}

public PageReference step2()
{
    tempAcc = [SELECT Id, Name, countOpenOpportunities__c FROM Account WHERE Id =: opportunity.AccountId];
    // Set the Opportunity name here based on what was set in step1
    if (opportunity.Name == null)
    {
        //Set the opportunity name to a standard name
        opportunity.Name = opportunity.Type + ' ' + opportunity.Service_Type__c + ' Program for ' + tempAcc.Name;
    }

    System.debug('In step2, tempAcc.Id: ' + tempAcc.Id);
    System.debug('In step2, opportunity.AccountId: ' + opportunity.AccountId);

    return Page.newOpportunityStep2;
}

public PageReference step3()
{
    //Set the roles to the opportunity
    if (role1.ContactId != null)
    {
        contact1 = [SELECT Id,Name,Title FROM Contact WHERE Id =: role1.ContactId];
    }

    if (role2.ContactId != null)
    {
        contact2 = [SELECT Id,Name,Title FROM Contact WHERE Id =: role2.ContactId];
    }

    if (role3.ContactId != null)
    {
        contact3 = [SELECT Id,Name,Title FROM Contact WHERE Id =: role3.ContactId];
    }

    if (role4.ContactId != null)
    {
        contact4 = [SELECT Id,Name,Title FROM Contact WHERE Id =: role4.ContactId];
    }

    if (role5.ContactId != null)
    {
        contact5 = [SELECT Id,Name,Title FROM Contact WHERE Id =: role5.ContactId];
    }       

    return Page.newOpportunityStep3;
}

// This method cancels the wizard, and returns the user to the 
// Opportunities tab
public PageReference cancel()
{
    PageReference opportunityPage = new ApexPages.StandardController(opportunity).view();
    opportunityPage.setRedirect(true);
    return opportunityPage; 
}

// This method performs the final save for all four objects, and
// then navigates the user to the detail page for the new
// opportunity.
public PageReference save()
{
    opportunity.accountId = tempAcc.id;
    insert opportunity;

    if(role1.ContactId != null)
    {
        role1.opportunityId = opportunity.id;
        insert role1;
    }
    if(role2.ContactId != null)
    {
        role2.opportunityId = opportunity.id;
        insert role2;
    }
    if(role3.ContactId != null)
    {
        role3.opportunityId = opportunity.id;
        insert role3;
    }
    if(role4.ContactId != null)
    {
        role4.opportunityId = opportunity.id;
        insert role4;
    }
    if(role5.ContactId != null)
    {
        role5.opportunityId = opportunity.id;
        insert role5;
    }

    // Finally, send the user to the detail page for 
    // the new opportunity.

    PageReference opptyPage = new ApexPages.StandardController(opportunity).view();
    opptyPage.setRedirect(true);

    return opptyPage;
}
}

Here's my class for the custom contact lookup page:

public with sharing class CustomContactLookupController {

public Contact contact {get;set;} // new contact to create
public List<Contact> results {get;set;} // search results
public string searchString {get;set;} // search keyword
private final Id currentUserId; //logged in user's Id
private final Account myAcct;
private final createNewOpportunity myReference;

public CustomContactLookupController(createNewOpportunity myController) {
    contact = new Contact();
    this.myReference = myController;

    System.debug('myReference.opportunity.AccountId: ' + myReference.opportunity.AccountId);
    System.debug('myReference.tempAcc.Id: ' + myReference.tempAcc.Id);
    System.debug('myReference.tempAcc.Name: ' + myReference.tempAcc.Name);

    myAcct = myReference.tempAcc;

    System.debug('myAcct Id: ' + myAcct.Id);
    System.debug('myAcct Name: ' + myAcct.Name);
    //this.myAcct = myController.getTheAccount();
    //System.debug('Does myAcct have an ID now? ' + myAcct.Id);
    //System.debug('Can I access a property on myController? ' + myController.tempAcc.Id);
    currentUserId = UserInfo.getUserId();
    // get the current search string
    searchString = System.currentPageReference().getParameters().get('lksrch');
    runSearch();

}   

// performs the keyword search
public PageReference search()
{
    runSearch();
    return null;
}

// prepare the query and issue the search command
private void runSearch()
{
    // TODO prepare query string for complex searches & prevent injections
    results = performSearch(searchString);               
} 

// run the search and return the records found. 
private List<Contact> performSearch(string searchString)
{
    String soql = 'SELECT id, name FROM Contact';
    if(searchString != '' && searchString != null)
    {
        soql = soql + ' WHERE name LIKE \'%' + searchString + '%\'';
        soql = soql + ' AND OwnerId =: currentUserId';
        soql = soql + ' AND AccountId =: myAcct.Id';
        soql = soql + ' ORDER BY LastViewedDate DESC';
        soql = soql + ' LIMIT 50';
    } else
    {
        soql = soql + ' WHERE OwnerId =: currentUserId';
        soql = soql + ' AND AccountId =: myAcct.Id';
        soql = soql + ' ORDER BY LastViewedDate DESC';
        soql = soql + ' LIMIT 50';
    }
    return database.query(soql); 
}

// save the new contact record
public PageReference saveContact()
{
    insert contact;
    // reset the contact
    contact = new Contact();
    return null;
}

// used by the visualforce page to send the link to the right dom element
public string getFormTag()
{
    return System.currentPageReference().getParameters().get('frm');
}

// used by the visualforce page to send the link to the right dom element for the text box
public string getTextBox()
{
    return System.currentPageReference().getParameters().get('txt');
}
}

And then finally the top of my VF page for the custom contact lookup pop-up window looks like the following:

<apex:page controller="createNewOpportunity" extensions="CustomContactLookupController"
title="Search" 
showHeader="false" 
sideBar="false" 
tabStyle="Contact" 
id="pg">

...my VF page stuff...

</apex:page>

The error I'm getting currently is the following:

Visualforce Error

System.QueryException: Variable does not exist: myAcct.Id

Keep in mind that I'm a beginner (both as a developer and with apex) and still learning a lot of these concepts and there is a lot that still isn't completely clear to me.

Thanks for any help you can provide!

share|improve this question
    
Where is the error happening? is it something simple where you are trying to call this.myAcct from CustomContactLookupController (instead of from in createNewOpportunity)? –  AmatorVitae Dec 13 '13 at 15:01
    
Sorry, I should have provided that info. The exact error message is: System.QueryException: Variable does not exist: myAcct.Id Class.CustomContactLookupController.performSearch: line 66, column 1 Class.CustomContactLookupController.runSearch: line 43, column 1 Class.CustomContactLookupController.<init>: line 28, column 1 –  Zigrivers Dec 13 '13 at 15:06
    
So the offending lines are as follows: runSearch(); (in the Constructor for CustomContactLookupController) results = performSearch(searchString); (in the runSearch method for the extension class) return database.query(soql); (in the method performSearch for the extension class) –  Zigrivers Dec 13 '13 at 15:07
    
We have fixed the error thanks to @amatorVitae, but the lookup isn't working yet because I'm unable to get the value of Account ID from the first step in the wizard for some reason. When I check the values of my variables for Account ID they are all "null" in my extension class, yet they have values in my controller class. So the only thing I can assume is that I'm doing something wrong when it comes to passing a reference to my controller class to my extension class? –  Zigrivers Dec 13 '13 at 15:51

1 Answer 1

From the Error Lines, the error is happening here:

private List<Contact> performSearch(string searchString)
{
    String soql = 'SELECT id, name FROM Contact';
    if(searchString != '' && searchString != null)
    {
        soql = soql + ' WHERE name LIKE \'%' + searchString + '%\'';
        soql = soql + ' AND OwnerId =: currentUserId';
        soql = soql + ' AND AccountId =: myAcct.Id';
        soql = soql + ' ORDER BY LastViewedDate DESC';
        soql = soql + ' LIMIT 50';
    } else {
        soql = soql + ' WHERE OwnerId =: currentUserId';
        soql = soql + ' AND AccountId =: myAcct.Id';  // <----- ERROR HERE
        soql = soql + ' ORDER BY LastViewedDate DESC';
        soql = soql + ' LIMIT 50';
    }
    return database.query(soql); 
}

The issue is that you can't reference an object's variable when you use database.query(soqlQueryString) for some string soqlQueryString.

Try this:

    private List<Contact> performSearch(string searchString)
{
    String soql = 'SELECT id, name FROM Contact';

    id myAcctId = this.myAcct.id;  // <-------------  ADDED

    if(searchString != '' && searchString != null)
    {
        soql = soql + ' WHERE name LIKE \'%' + searchString + '%\'';
        soql = soql + ' AND OwnerId =: currentUserId';
        soql = soql + ' AND AccountId =: myAcct.Id';
        soql = soql + ' ORDER BY LastViewedDate DESC';
        soql = soql + ' LIMIT 50';
    } else {
        soql = soql + ' WHERE OwnerId =: currentUserId';
        soql = soql + ' AND AccountId =: myAcctId';  // <----- CHANGED
        soql = soql + ' ORDER BY LastViewedDate DESC';
        soql = soql + ' LIMIT 50';
    }
    return database.query(soql); 
}

And on a side note, you can wrap areas that might throw an error with a try / catch statement.

Example: Though this wouldn't solve the error you posted, this would have at least allowed a cleaner way of catching the error;

private List<Contact> performSearch(string searchString){
    list<contact> returnList = new list<contact>();
    // ...
    if {  
        // ...
    } else {
        // ...
    }

    try {
        returnList=(list<contact>)database.query(soql);
    } catch(exception e){
        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'An error occurred QUERYING performSearch('+searchString+').  ERROR = '+string.valueOf(e)));
        return new list<contact>();
    }
    return returnList;
}
share|improve this answer
1  
Wow! Thank you. This solved the problem and I wouldn't have realized it without our help as I was flailing about in the wrong part of my code. So glad the error was here vs. where I thought it was because it was doing my head in. Thanks again! –  Zigrivers Dec 13 '13 at 15:27
    
So this fixed the error, but I still have a problem with passing the Account ID from the controller to the extension class. My debug log shows the myAcctID as "null". So, for some reason I am not able to get access to the Account ID the user selects in the first step of the wizard. Ugh! –  Zigrivers Dec 13 '13 at 15:48
    
Originally, I thought that was the issue. In createNewOpportunity, are you ever INSERTing tmpAcct? (especially before CustomContactLookupController copies its value?) –  AmatorVitae Dec 13 '13 at 15:54
    
No, not until the user hits the save button, which is on the third step where I take them to a page to show them the results of everything they have entered/selected for the opportunity. –  Zigrivers Dec 13 '13 at 16:03
    
When the Visualforce Page is initialized, it's creating both the Controller and all Extensions at the SAME time. Therefore, when your Extension is initialized, tmpAcct from the Controller hasn't been INSERTed yet, so it has no id. If you want to use the Extension you have to INSERT tmpAcct first in your Controller Constructor so that the Extension sets the value with the same Id: myAcct = myReference.tempAcc; –  AmatorVitae Dec 13 '13 at 16:41

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.