[SalesForce] Create a custom button on a custom related list that simply adds other opportunities to that list

I've created a custom lookup field on our opportunity object with and included a custom related list that will show related records that are linked to that 'master' opportunity.

How to I add a custom button on that related list called 'Add' that just takes the user to a visualforce page where they can use a lookup field to search, add, and save that record to the related list on the opportunity?

I've been learning visualforce all day on trialhead and this is what I've come up with so far (but it's not creating a lookup field):

Visualforce Page:

<apex:page docType="html-5.0" standardController="Opportunity">
<apex:form>
    <apex:pageBlock title="Add Opportunity">
        <apex:pageBlockSection>
            <apex:input value="{!Opportunity.Id}"/>
        </apex:pageBlockSection>
        <apex:pageBlockButtons>
            <apex:commandButton action="{!save}" value="Save" />
        </apex:pageBlockButtons>
    </apex:pageBlock>
</apex:form>

Best Answer

I think, per our conversation in comments, that we're on the same page here: what you're aiming to do is add a button that takes the user to a page where they can associate existing child records to the parent Opportunity in a single operation ("Problem A"), which is a different problem than pre-populating a custom lookup field on a single, newly-created child record ("Problem B").

Unfortunately, that means the neat and easy solutions for Problem B do not apply here! Instead, you're going to end up doing some more custom code, and it may be more than you expect to get this done - depending on the exact details of your requirements.

Fundamentally, to solve Problem A, you're going to need a Visualforce page and an Apex controller (as well as an Apex unit test for the controller).

It sounds like what you'd like to do is use the system-provided lookup dialogue to choose which Opportunity you'd like to relate to your selected parent record. That raises some interesting complexities, because you're really trying to do the lookup backwards (starting from the parent and finding a child to link to it). If you don't have lookup field filters on the lookup field, I think it'll work, though. Here's a rough sketch of how I'd try this, and you can fill in the details.

In your controller (I would do this as a controller extension), declare an instance variable public Opportunity opp { get; set; }. We'll use this Opportunity as a sort of mock to convince Visualforce to render the lookup for us. In your constructor, initialize the variable:

public MyControllerExtension(ApexPages.StandardController ctlr) {
    opp = new Opportunity();
    this.ctlr = ctlr; // make sure to declare this instance variable.
}

Then, in Visualforce, you'll change the markup you already have just a bit.

<apex:pageBlock title="Add Opportunity">
    <apex:pageBlockSection>
        <apex:input value="{!opp.My_Lookup_Field__c}"/>
    </apex:pageBlockSection>
    <apex:pageBlockButtons>
        <apex:commandButton action="{!save}" value="Save" />
    </apex:pageBlockButtons>
</apex:pageBlock>

So Visualforce is going to render a lookup field here for the field on that empty Opportunity sObject variable we created, and provide the system lookup dialogue.

Now, in save(), we do a little trick.

public PageReference save() {
    opp.Id = opp.My_Lookup_Field__c;
    opp.My_Lookup_Field__c = (Opportunity)ctlr.getRecord().get('Id');

    update opp;
}

We grab the Id of the record that we selected (which Visualforce populated into the lookup field on this sObject record) and pop it into the Id field on the variable - that's the record we actually want to update. Then, we take the Id of the record that we started this page interaction with (the parent Opportunity) and populate it into the lookup field in opp.

Then we just update opp. The net effect is that we've populated the lookup field on that existing Opportunity.

(Code above typed in SE, not tested, but I think this approach could work for a low-code fix. The bulk case could be an elaboration once you get this running).