How to Dynamically Populate Fields in Salesforce Data Entry Screens Using Apex Classes

I recently had to solve this use case for a work project and I thought I would share as it could be very useful across a number of scenarios within  Salesforce. Why this is really useful and powerful is that it works on a New Object screen BEFORE the save occurs. So you can see results while the screen is still in edit mode.

The application has two custom objects, Proposal and Project, that are related with a lookup from Project to Proposal. While the relationship  is not Master-Detail, the Proposal lookup field is required on the Project screen so that a Proposal is required before a Project can be created.  Both Proposal and Project have fields in common of Title, Account, Contact, and Scope of Work. Title, Account, and Contact are required on both objects.

The Use Case is that when creating a new Project all four of these fields should copy over from the related Proposal so the user doesn’t have to reenter them,  while remaining editable on the Project data entry screen, all before saving the Project. So when the New Project button is clicked, and the related Proposal is selected, all of the related common field values from the Proposal should immediately copy over into matching fields on the Project screen prior to clicking the Project save button.  Workflow field updates and triggers don’t work for this scenario because (1) we don’t want to first have to click save and then go back into edit before making changes and finish filling out other fields on the New Project screen. And (2) since these fields are required on the Project screen the record won’t save in the first place unless all of the required fields contain values.

proposalexample

 

The solution has to work in two different cases when creating a new Project. One is clicking the Project tab and then selecting the New Project button. In this scenario the user will need to first select the related Proposal after which the related fields should be copied over from Proposal to Project. The other case occurs when starting from the Proposal screen and clicking the New Project button in the related project list. In this case the Proposal__c.Name is automatically copied over to the related Proposal__c field on new Project screen and so is unnecessary for the user to select. Our solution has to work correctly for both of these cases.

The solution for this problem is to create a Visualforce page with an extended controller class that replaces the standard New Project page. Three files have to be created to make this work, a Visualforce page,  an Apex class, and a unit test. Salesforce requires you to run a unit test on all code before you are able to deploy from sandbox into production.

The VisualForce Page

I won’t go into how to create a VisualForce page here, but I will mention a free tool I use which makes turning a standard or custom object page into a VisualForce page a snap.  It’s called Layout Page and can be found in the App Store. It converts any Salesforce Page Layout (Standard OR Custom) into a Standard Visualforce Page instantly.

Once you have created the VisualForce page the following changes should be made.

The first line of the VisualForce page shown below sets the controller and extension for the page. The main controller is the Project__c object and the Apex extension class is named RelatedController1.

<apex:page standardController="Project__c" extensions="RelatedController1">

This next line of code calls the Apex function PopulateProject() whenever a user selects the related Proposal. The PopulateProject() method will locate the selected Proposal record in Proposal__c and copy the fields, Title, Contact, Account, and Scope_of_Work from the selected Proposal record into the appropriate fields on the new Project screen. This line handles the scenario where the user selects the New Project button from the Projects tab and so on the New Project screen the related Proposal is blank and needs to be manually selected.

<apex:inputField value="{!Project__c.Proposal__c}">
            <apex:actionSupport event="onchange" action="{!PopulateProject}" rerender="accinfo, msgs"/> 
         </apex:inputField>

The following line handles the scenario where the user selects New Project button from the Project related list on the Proposal screen where the Proposal Name will already be filled in and so not necessary to select. This line calls an inline Ajax function located at the bottom of the VisualForce page that checks to see if the  Project__c.Proposal__c field is NOT null, in which case it calls the Apex method PopulateProject to copy in all of the Proposal field values.

<apex:actionFunction name="CallApexMethod" action="{!PopulateProject}" reRender="accinfo, msgs"/>

The Ajax code below should be located at the bottom of the VisualForce page just above the </apex:page> tag. This script executes on every page load, which is what we need in order to check if the related Proposal has already been selected.

<script>
    function addLoadEvent(func) 
{ 
  var oldonload = window.onload; 
  if (typeof window.onload != 'function') 
  { 
     window.onload = func; 
  } 
  else
  { 
      window.onload = function()  
      { 
        if (oldonload) 
        { 
           oldonload(); 
        } 
        func(); 
      } 
   } 
} 
   
addLoadEvent(function()  
{ 
var controllervariable='{!Project__c.Proposal__c}';
if(controllervariable==null){return false;}
else {
  CallApexMethod(); }
});
</script>

The Apex Class

The Apex class, RelatedController1, is shown below. This where all the work happens to locate the selected Proposal record and set fields in the new Project record equal to fields from the selected Proposal record.

public with sharing class RelatedController1
{
public Proposal__c prop {get;set;}

private ApexPages.StandardController stdCtrl;

public RelatedController1(ApexPages.StandardController std)
{
stdCtrl=std;
}

public void PopulateProject()
{
Project__c proj=(Project__c) stdCtrl.getRecord();
if(proj.Proposal__c == null){return;}
else{
prop=[select Name,  Account__c, Scope_of_Work__c, Contact__c from Proposal__c where Id=:proj.Proposal__c];

proj.Name=prop.Name;
proj.Account__c=prop.Account__c;
proj.Scope_of_Work__c=prop.Scope_of_Work__c;
proj.Project_Contact__c=prop.Contact__c;}
}
}

All of the work described so far has to be completed in a Salesforce sandbox or developer account. I used a  partial sandbox under my Production org which copies a subset of data into the sandbox along with all of the Production org metadata.

The Unit Test

Once everything is working correctly in sandbox, you will have to create a unit test that when run exercises at least 80% of the code in the Apex class without error. Only then will Salesforce allow you to create an outbound change set and deploy it into your Production org.

The code for the test class is shown below.

@isTest(SeeAllData=true)

public class RelatedController1Test {

static testMethod void testRelatedController1() {
// Select an actual proposal record from Proposal__c
Proposal__c p = [SELECT Id, Name FROM Proposal__c WHERE       Name=’Test Proposal Dekra’ LIMIT 1];
// Clone the selected proposal record and set some of the fields
Proposal__c testProposal = p.clone();

testProposal.Name = ‘Acme Test’;
testProposal.Scope_of_Work__c = ‘test’;
// Create the test proposal record
insert testProposal;

// Create the test project record and set the related Proposal__c field equal to the Id of the newly created test Proposal__c.Id.
Project__c prj = new Project__c();
prj.Name = ‘test’;
prj.Proposal__c = p.Id;

insert prj;
// Now run the test by instantiating the RelatedController1 class and calling the PopulateProject() method. This test gives 100% coverage.
Test.startTest();
ApexPages.Standardcontroller std = new ApexPages.Standardcontroller(prj);
RelatedController1 clsObj = new RelatedController1(std);
clsObj.PopulateProject();
Test.stopTest();
}
}

When the test has run successfully you are ready to create an outbound Change Set containing  two files, the RelatedController1 Apex class file and the Visualforce page, and deploy them into your Production org.

Leave a Reply

Your email address will not be published. Required fields are marked *