+ Start a Discussion
Jwdv22Jwdv22 

Show Parent Account's Contacts on Child Accounts Contact related list

Hello,

 

In our industry we have contacts who by for multiple accounts. We are currenlty cleaning up all these duplicate contacts since they are all assigned to multiple accounts and moving towards a model that contacts who buy for multple accounts are on the Parent Account. But they still want to see the buyers in the child accounts related list. The contacts shouldn't exist on the child, only be visible.

 

If did a search before posting, and found this but the post was old and I wasn't sure if anyone would see my reply since the original OP didn't report back whether that solution worked for them. Any help with this would be appreciated as I am new to developing for SalesForce.

Thanks

http://forums.sforce.com/t5/New-to-Cloud-Development/Show-Parent-Account-Contacts-from-Child-Account-records/td-p/234403

Best Answer chosen by Admin (Salesforce Developers) 
bob_buzzardbob_buzzard

An extension controller simply has an additional constructor that takes the standard controller as a parameter.  Further, the standard controller contains the record that is being edited/viewed etc.  

 

E.g.

 

    private Account a;
    
    public AccountExtension(ApexPages.StandardController stdController) {
        a = (Account)stdController.getRecord();
    }

 

If your page is viewing/editing an existing account, it will be passed on the id parameter to the standard controller.  

All Answers

bob_buzzardbob_buzzard

The principle is correct, although I think there's a bug where the contact is added to the map, as it just puts an empty list rather than a list containing the single contact.  You'll also need to put an iterator into the visualforce page - a pageblocktable or datatable would do it.

 

If you want to have a stab and post up any problems I'll be glad to help.

Jwdv22Jwdv22

Well I eventually did figure it out, but since then the project has changed to require that any contact marked as a "Buyer" customer field accross any Account with the same Parent Account ID (I need to change to a custom field because I can't  reuse Parent Account. I need this field filled in even on the Parent Account and SalesForce won't allow  an Account to denote itself as the Parent). So if anyone else needs this or has questions let me know, but here is the solution.

 

VF page

<apex:page controller="mycontroller2" tabStyle="Account">
 <apex:detail relatedList="true" title="true">
<apex:pageBlock title="Buyers">


<apex:pageBlockTable value="{!pcons}" var="pcontacts"> 
 <apex:column headervalue="Name">
              <apex:outputLink value="/{!pcontacts.id}">{!pcontacts.Name}</apex:outputLink>
         </apex:column> 
          <apex:column value="{!pcontacts.Ext_Id__c}"/>
          <apex:column >
              <apex:outputLink value="/{!pcontacts.Account.id}">{!pcontacts.Account.Name}</apex:outputLink>
         </apex:column>

</apex:pageBlockTable>

 </apex:pageBlock>

 </apex:detail>
</apex:page>

 VF Controller 

public class mycontroller2 {
private List<Contact> pcons;


public List<Contact> getPcons() 
{
List<Account> ac=[select id,Name,parentid from Account where parentid=:ApexPages.currentPage().getParameters().get('parentid')];
// List<Account> ac=[select id,parentid from Account];
 Set<Id> ids=new Set<Id>();
{ 
for(Account a:ac)
{
ids.add(a.id);    // collect the parent accountid for each account in a set
 }
 } 
//collect the all contacts related to these parent accountid
List<contact> pcons=[select id,Name,Ext_Id__c, accountId,Account.Name from contact where Buyer__c=True and Account.parentid in:ids];
return pcons;
}
}

 

I found this pieces of this code online, but I don't recall where. So I am sorry if you recognize parts of your code and I did not attribut credit to your or your blog post, but thanks for your help.

 

Two questions I do have:

Is if there is any way to move my NEW Buyers pageblocktable to the top of the related lists without having to change my VFPage to list each individual related list?

Am I supposed to make my Controller into an object or something? I tricked SFDC into allowing my override page, but I would prefer not tohave to trick SFDC. What step am I missing? Thanks

bob_buzzardbob_buzzard

First question - no.  The apex:detail stuff comes as one block, you can't insert your own elements into it.

 

Second question - not sure what you mean by trick.  If you couldn't see your page as an override option, that would be because you only see pages that use the account standard controller.  If that is the case, you could change yours to an extension controller and have the standard controller back the page.

Jwdv22Jwdv22

Thanks for the quick reply.

First Question: -> So I could get around this by changing the apex detail lists to false and then just do <apex:relatedlist list"Contacts" />

 

Second question: -> That would be exactly why I couldn't see it. I did trick it by adding it as a standard override and then changing it. How to I change my controller to a extension controller?

bob_buzzardbob_buzzard

An extension controller simply has an additional constructor that takes the standard controller as a parameter.  Further, the standard controller contains the record that is being edited/viewed etc.  

 

E.g.

 

    private Account a;
    
    public AccountExtension(ApexPages.StandardController stdController) {
        a = (Account)stdController.getRecord();
    }

 

If your page is viewing/editing an existing account, it will be passed on the id parameter to the standard controller.  

This was selected as the best answer
Jwdv22Jwdv22

Great after doing this and creating it as a controller extension I was able to answer and do question 1!!!

 

 

Thanks for your help.

Jwdv22Jwdv22

 

Sorry I didn't read the second part about having to pass in the account. When I do include the private Account a;

in the first line of the extension I get an error

 

Ideas? Thanks

 

Error: mycontroller2 Compile Error: unexpected token: 'Account' at line 1 column 8

bob_buzzardbob_buzzard

That looks like a simple syntax error - can you post that updated section of code?

Jwdv22Jwdv22

I appologize for my ignorance. Just trying  to learn and I appreciate your help.

 

 

  private Account a;
    public mycontroller2(ApexPages.StandardController controller) {
    a = (Account)stdController.getRecord();
    }
public class mycontroller2 {

private List<Contact> pcons;

public List<Contact> getPcons() 
{
List<Account> ac=[select id,Name,parentid from Account where parentid=:ApexPages.currentPage().getParameters().get('parentid')];
// List<Account> ac=[select id,parentid from Account];
 Set<Id> ids=new Set<Id>();
{ 
for(Account a:ac)
{
ids.add(a.id);    // collect the parent accountid for each account in a set
 }
 } 
//collect the all contacts related to these parent accountid
List<contact> pcons=[select id,Name,Ext_Id__c, accountId,Account.Name from contact where Buyer__c=True and Account.parentid in:ids];
return pcons;
}
}

 

bob_buzzardbob_buzzard

No worries - learning is encouraged here!

 

This is declaring an instance variable, so needs to be inside the class, as does the constructor.  You'll also need to change your soql to use the id of the account variable "a", as the override won't pass a parameter of parentId.

 

public class mycontroller2{  
private Account a;

public mycontroller2(ApexPages.StandardController controller) { a = (Account)stdController.getRecord(); }
Jwdv22Jwdv22

Getting closer. So I changed my Account List like you said from 

// List<Account> ac=[select id,Name,parentid from Account where parentid=:ApexPages.currentPage().getParameters().get('parentid')];

to

List<Account> ac=[select id,Name,parentid from Account where parentid=:b.parentid];

 

***WHOOPS*** edited  for forgot :b.parentid

Getting this error now

System.SObjectException: SObject row was retrieved via SOQL without querying the requested field: Account.ParentId

 

Class.mycontroller2.getPcons: line 9, column 1 

bob_buzzardbob_buzzard

There's a couple of things here.  Firstly you need to bind the a.parentId using the colon notation:

 

List<Account> ac=[select id,Name,parentid from Account where parentid <>'' and parentid= :a.parentid];

 

Secondly, the contained account gets all fields that are used by visualforce page rather than all fields.

 

if you add the following somewhere in your page:

 

<apex:outputField value="{!account.parentId}" rendered="false"/>

 

 

that will make it available to your controller.

Jwdv22Jwdv22

Wow, now it is doing something I completely don't understand.

Even though I already have the Parent Account field visible on Accounts, I added your 

<apex:outputField value="{!account.parentId}" rendered="false"/>

and I think that got rid of my SOQL error from above.

 

When I have 

List<Account> ac=[select id,Name,parentid from Account where parentid <> '' and parentid=:a.parentid];

None of my Buyers show up, at all, on any accounts.

When I remove the parentid<>'' from the where clause I get all the buyers on Accounts without parentid's and no buyers on accounts with parentid's.

 

That coudn't be more backwards. HA.

<apex:page standardController="Account" extensions="mycontroller2">
<apex:outputField value="{!account.parentId}" rendered="false"/>
<!-- <apex:page controller="mycontroller2" tabStyle="Account"> -->
<apex:detail relatedList="false" title="true">
<apex:relatedlist list="NotesAndAttachments"/>
<apex:relatedList list="Contacts"/>
<apex:pageBlock title="Buyers">

<apex:pageBlockTable value="{!pcons}" var="pcontacts"> 
 <apex:column headervalue="Name">
              <apex:outputLink value="/{!pcontacts.id}">{!pcontacts.Name}</apex:outputLink>
         </apex:column> 
          <apex:column value="{!pcontacts.Ext_Id__c}"/>
          <apex:column >
              <apex:outputLink value="/{!pcontacts.Account.id}">{!pcontacts.Account.Name}</apex:outputLink>
         </apex:column>

</apex:pageBlockTable>


</apex:pageBlock>
<apex:relatedlist list="openactivities"/>
<apex:relatedlist list="activityhistories"/>
<apex:relatedlist list="End_Users__r"/>
<apex:relatedlist list="RMAs__r"/>


</apex:detail>

</apex:page>

 

public class mycontroller2 {
    private Account a;
    public mycontroller2(ApexPages.StandardController stdcontroller) {
    a = (Account)stdController.getRecord();
    }
private List<Contact> pcons;
public List<Contact> getPcons() 
{
// List<Account> ac=[select id,Name,parentid from Account where parentid <> '' and parentid=:a.parentid];
List<Account> ac=[select id,Name,parentid from Account where parentid=:a.parentid];
// List<Account> ac=[select id,Name,parentid from Account where parentid=:ApexPages.currentPage().getParameters().get('parentid')];
// List<Account> ac=[select id,parentid from Account];
 Set<Id> ids=new Set<Id>();
{ 
for(Account a:ac)
{
ids.add(a.id);    // collect the parent accountid for each account in a set
 }
 } 
//collect the all contacts related to these parent accountid
List<contact> pcons=[select id,Name,Ext_Id__c, accountId,Account.Name from contact where Buyer__c=True and Account.parentid in:ids];
return pcons;
}
}

 

bob_buzzardbob_buzzard

I must admit I've slightly lost track of what this code is trying to achieve.

 

As it stands it will retrieve all accounts that have the same parent as the account contained by the controller.  From what you say, it sounds like the account in the controller has a null parentId.

Jwdv22Jwdv22
Yes that is what I thought it would do. I am trying to get a list of all accounts with the same parentid then later I select contacts from that list that are flagged as buyers. But when I go to an account with a parentid (and a known sister account with the same parentid) the buyers are blanks or empty. However of I go to an account with no parentid I get the list of all buyers. It is like it is working backwards.
bob_buzzardbob_buzzard

Change the rendered value to true for the outputfield added a couple of posts ago - that will show you if the account has a parent defined.

Jwdv22Jwdv22

Yes the Parent Account shows up twice now, once becaues we are rendering it, and once in the Account Detail because that is where we always show it. However, I still don't see Buyers on Accounts with a Parent Account. Is three someway to debug and follow the variables. I don't think the Accounts ParentID is being passed through. I also changed the a variable to b since I use a  here

 

for(Account a:ac)
{
ids.add(a.id);    // collect the parent accountid for each account in a set
 }
 } 
//collect the all contacts related to these parent accountid
List<contact> pcons=[select id,Name,Ext_Id__c, accountId,Account.Name from contact where Buyer__c=True and Account.parentid in:ids];
return pcons;

 Didn't help though.

Jwdv22Jwdv22

Figured it out. After looking at the code again. I changed the 

List<contact> pcons=[select id,Name,Ext_Id__c, accountId,Account.Name from contact where Buyer__c=True and Account.id in:ids];

from Account.parentid

At that point I also readded the parenltid <>'' and it is working great.

 

Thanks for your help, now I just have to finish tweaking the layout. Like including the edit/delete buttons, and adding Chatter Follow buttons to the VF page.

 

Thanks again.

bob_buzzardbob_buzzard

Glad to hear you got there in the end!

Jwdv22Jwdv22

Hi Bob,

 

Sorry to get this thread open again, but I am trying to test my controller extension and I am not sure what to test? It works fine in my sandbox.

 

Here is the Controller, and I am assuming to get to 76% I have to test the List<Contact> 's 

public class mycontroller2 {
    private Account b;
    public mycontroller2(ApexPages.StandardController stdcontroller) {
    b = (Account)stdController.getRecord();
    }
 
private List<Contact> pcons;
public List<Contact> getPcons() 
{

List<Account> ac=[select id,Name,parentid from Account where (ParentAccount__c <> '' and ParentAccount__c=:b.ParentAccount__c)];
 Set<Id> ids=new Set<Id>();
{ 
for(Account a:ac)
{
ids.add(a.id);    // collect the parentid for each account in a set
 }
 } 
//collect the all contacts related to these parent accountid
List<contact> pcons=[select id,Name,Ext_Id__c, accountId,Account.Name,Active__c,Contact_Title__c,Email,Phone from contact where Buyer__c='Y' and Account.id in:ids];
return pcons;
}
}

 And here is the VFpage

<apex:page standardController="Account" extensions="mycontroller2">
<apex:outputField value="{!account.ParentAccount__c}" rendered="false"/>
<apex:detail relatedList="true" relatedListHover="true" title="true" showChatter="true" inlineEdit="true" /> 
<apex:form >
<apex:commandButton value="Save" action="{!save}" id="saveButton" />
</apex:form>
<apex:pageBlock title="Buyers">
<!-- <apex:pageBlockButtons >
<apex:commandButton value="Save" action="{!save}" id="saveButton" />
<apex:commandButton value="Cancel" action="{!cancel}" id="cancelButton"   / 
</apex:pageBlockButtons>  -->

    <apex:pageBlockTable value="{!pcons}" var="pcontacts" > 
<!-- Add Sorting capability and Show more/Go to List links -->   
                <apex:column headervalue="Action" width="90">
                    <apex:outputLink value="{!URLFOR($Action.Contact.Edit, pcontacts.Id)}">Edit</apex:outputLink> 
        | 
                <chatter:follow entityId="{!pcontacts.id}"/>
                </apex:column> 
                <apex:column headervalue="Contact Name">
                    <apex:outputLink value="/{!pcontacts.id}">{!pcontacts.Name}</apex:outputLink>
                </apex:column>
                <apex:column value="{!pcontacts.Active__c}"/>
                <apex:column value="{!pcontacts.Contact_Title__c}"/>
                <apex:column value="{!pcontacts.Email}"/>
                <apex:column value="{!pcontacts.Phone}"/>
<!-- Ditch in productions -->    <apex:column value="{!pcontacts.Ext_Id__c}"/>
                <apex:column headervalue="Account">
                     <apex:outputLink value="/{!pcontacts.Account.id}">{!pcontacts.Account.Name}</apex:outputLink>
                </apex:column>
<!-- <apex:inlineEditSupport event="ondblClick"
showOnEdit="saveButton,cancelButton" hideOnEdit="editButton" / -->
    </apex:pageBlockTable>
  
</apex:pageBlock>
</apex:page>

 

bob_buzzardbob_buzzard

Have you started a test class that you can post?

Jwdv22Jwdv22

This is the testclass I use for our Triggers, so I just added to the end of it. The testMethod newAccountpage_Test is the first one I created and got to work without errors or problems, but it doesn't list my controller extension in the Code Coverage after I run the tests. I am assuming that if the test of my controller went successful it would list in  the Apex Test Runner mycontroller2 (ApexClass) # of lines tested, 100% covered. Correct?

 

The last one, testMyController is where I started down different path from a post I found but it doesn't work because I don't have a Save Method in my Controller, but I was starting to think I could Edit the page's Account description  then somehow call the built in Save button. But this is defintely confusing.

 

/**
 * This class contains unit tests for validating the behavior of Apex classes
 * and triggers.
 *
 */
@isTest
private class OC_Trigger_test {
    static testMethod void Ownershipchange() {
        User u118 = [select Id from User where Sales_Number__c ='118'];
    	User u707 = [select Id from User where Sales_Number__c ='707'];
        Account aRum = [select id, name, OwnerID from account where Account_Number__c='989800'];
	        aRum.Salesperson_Code__c = '118';
	        aRum.OwnerId = u118.Id ;
        	aRum.ParentAccount__c = '984434';
        update aRum;
    }
    static testMethod void myUnitTest() {
    	User u100 = [select Id from User where Sales_Number__c ='100'];
    	User u707 = [select Id from User where Sales_Number__c ='707'];
        Account a = new Account(Name='test account',
	        Outside_Salesperson__c = u707.Id,
	        Salesperson_Code__c = '100',
	        OwnerId = u100.Id 
        );
        insert a;
        Contact c = new Contact(AccountId=a.Id,Lastname='test',Buyer__c ='Y');
        insert c;
    }


    static testMethod void QOCTest() {
    	QuotesOrdersCredits_Header__c qTest = null;
    	for ( QuotesOrdersCredits_Header__c q : [Select Id, (Select Id From Tasks where Subject like 'Open Quote%' and Status<>'Completed') From QuotesOrdersCredits_Header__c where Type__c='Quote' and Status__c='Open' order by LastModifiedDate desc limit 1000]) {
    		qTest = q;
    		if ( ! q.Tasks.isEmpty()) {
    			break;
    		}
    	}
    	QuotesOrdersCredits_Header__c nq = new QuotesOrdersCredits_Header__c( Id = qTest.Id, Status__c='Cancelled');
    	update nq;
    }
    static testMethod void newAccountpage_Test()
{
		//Test converage for the myPage visualforce page
		PageReference pageRef = Page.newAccountPage;
		Test.setCurrentPageReference(pageRef);
		Account newAccount = [Select Id,ParentAccount__c From Account where (Account_Number__c='984423' and ParentAccount__c = '984434')];
		//insert newAccount;
		//create first contact
		//Contact myContact = new Contact (FirstName='Joe',
		//LastName='Schmoed', Email='jschmoed@yahoo.com', Buyer__c ='Y',
		//AccountId=newAccount.id);
		//insert myContact;
		ApexPages.StandardController sc = new ApexPages.standardController(newAccount);
		// create an instance of the controller
		mycontroller2 myPageCon = new mycontroller2(sc);
		//try calling methods/properties of the controller in all possible scenarios
		//List <Contact> pContacts = myPageCon.getPcons();
                myPageCon.getPcons();         
}

public static testMethod void testMyController() {
       
       //Use the PageReference Apex class to instantiate a page
       PageReference pageRef = Page.newAccountpage;
       
       //In this case, the Visualforce page named 'success' is the starting point of this test method. 
       Test.setCurrentPage(pageRef);
       
       Account acct = [Select id, name, ParentAccount__c From Account a LIMIT 1];  
     
       //Instantiate and construct the controller class.   
		mycontroller2 controller = new mycontroller2(new ApexPages.StandardController(acct));

       //Example of calling an Action method. Same as calling any other Apex method. 
       //Normally this is executed by a user clicking a button or a link from the Visualforce
       //page, but in the test method, just test the action method the same as any 
       //other method by calling it directly. 

       //The .getURL will return the page url the Save() method returns.
       String nextPage = controller.save().getUrl();

       //Check that the save() method returns the proper URL.
       System.assertEquals('/apex/failure?error=noParam', nextPage);

       //Add parameters to page URL
       ApexPages.currentPage().getParameters().put('qp', 'yyyy');
     
       //Instantiate a new controller with all parameters in the page
       controller = new mycontroller2(); 

       //Example of calling the 'setter' method for several properties. 
       //Normally these setter methods are initiated by a user interacting with the Visualforce page, 
       //but in a test method, just call the setter method directly. 
       controller.setDescription('firstlast@acme.com');
       nextPage = controller.save().getUrl();

       //Verify that the success page displays
       System.assertEquals('/apex/success', nextPage); 
   }
}

 Thanks

bob_buzzardbob_buzzard

I think all that you are missing is a call to myController.getPCons() after you've instantiated the controller passing the standard controller in as a parameter.

 

 

 

 

Jwdv22Jwdv22

*EDIT sorry we posted at the same time so I didn't see your reply.

I did make that change before you posted and it does run. So maybe I am missing something about what should be be tested?

 

And here is the log file for my test

OC_Trigger_test.newAccountpage_Test
09:47:38.716 (7716714000)|METHOD_ENTRY|[23]|01p60000000GvXW|OC_Trigger_test.OC_Trigger_test()
09:47:38.716 (7716733000)|METHOD_EXIT|[23]|OC_Trigger_test
09:47:38.717 (7717157000)|SOQL_EXECUTE_BEGIN|[63]|Aggregations:0|select Id, ParentAccount__c from Account where (Account_Number__c = '984423' and ParentAccount__c = '984434')
09:47:38.725 (7725694000)|SOQL_EXECUTE_END|[63]|Rows:1
09:47:38.725 (7725923000)|METHOD_ENTRY|[1]|01pR0000000AX1V|mycontroller2.mycontroller2()
09:47:38.725 (7725939000)|METHOD_EXIT|[1]|mycontroller2
09:47:38.725 (7725979000)|CONSTRUCTOR_ENTRY|[72]|01pR0000000AX1V|<init>(ApexPages.StandardController)
09:47:38.734 (7734944000)|CONSTRUCTOR_EXIT|[72]|01pR0000000AX1V|<init>(ApexPages.StandardController)
09:47:38.734 (7734990000)|METHOD_ENTRY|[75]|01pR0000000AX1V|mycontroller2.getPcons()
09:47:38.735 (7735326000)|SOQL_EXECUTE_BEGIN|[11]|Aggregations:0|select id, Name, parentid from Account where (ParentAccount__c != '' and ParentAccount__c = :tmpVar1)
09:47:38.745 (7745615000)|SOQL_EXECUTE_END|[11]|Rows:5
09:47:38.746 (7746427000)|SOQL_EXECUTE_BEGIN|[20]|Aggregations:0|select id, Name, Ext_Id__c, accountId, Account.Name, Active__c, Contact_Title__c, Email, Phone from contact where (Buyer__c = 'Y' and Account.id = :tmpVar1)
09:47:38.764 (7764394000)|SOQL_EXECUTE_END|[20]|Rows:9
09:47:38.764 (7764506000)|METHOD_EXIT|[75]|01pR0000000AX1V|mycontroller2.getPcons()
09:47:39.425 (7764532000)|CUMULATIVE_LIMIT_USAGE
09:47:39.425|LIMIT_USAGE_FOR_NS|(default)|

 

It does get  the 9 rows of buyers that I am looking for so it is successful and IMO it is touching or running most of my controller, so why isn't it saying it tested it?

bob_buzzardbob_buzzard

Is this in eclipse?  I've found that since spring 12 was deployed it only shows my test classes as having coverage.  If that is the case, try running it in the UI.

Jwdv22Jwdv22

That was it!!! The IDE (Eclpise) just never listed it as having coverage, but the log showed it was running it. So Saved to Server and Ran test on the Sandbox and got 100% Coverage. Thanks so much.