Pass Values to New Opportunity Using Apex

Last year I wrote a post describing how to pass values to a new opportunity using the URL. This post is simply an alternative to accomplish the same goal of auto-populating values to a new record except using Visualforce & an Apex Class controller.

For the purposes of our demonstration we will make some general assumptions about the values we want to pass to the new record. To keep it simple we plan on populating the Name, Stage & Close Date of the new opportunity. We will also make the logic flexible enough so that if Salesforce happens to pass additional information we will be able to include those key value pairs as well.

At a high level we are going to build our logic as follows:

  • The User will click the "New Opportunity" button
    • Because we will be overriding the standard "New" button on the Opportunity object, the button is accessible from any standard/custom page where the "New" button resides. For example, the Contact detail page related list & the Account detail page related list
  • The override logic directs the User to newOppOverride.page Visualforce page
  • Action attribute in newOppOverride.page definition executes the newOppOverride.cls Apex logic
    • If Apex logic fails for any reason, the User stays on newOppOverride.page and errors are displayed
    • If Apex logic is successful, the User ends up on the standard Salesforce page for a New Opportunity record and values for some of the fields will be populated

Now that we have a handle on the logic we can build the Visualforce page.

<apex:page action="{!prepopulateValues}" extensions="newOpportunityOverride" standardController="Opportunity">
<!--
	Created by: Greg Hacic
	Last Update: 9 February 2016 by Greg Hacic
	Questions?: greg@interactiveties.com
	
	Notes:
		- allows for pre-population of specific Opportunity field values when the "New" Opportunity button is clicked
-->
	<apex:sectionHeader subtitle="Auto-populate Certain Fields" title="Opportunity"></apex:sectionHeader><!-- the header for the page, which is not necessary -->
	<apex:pageMessages></apex:pageMessages><!-- error messaging section for page - allows for display of any issues to the human -->
</apex:page>

A few notes about the Visualforce page:

  • We are using the standard controller for Opportunity because this makes the Visualforce page accessible as a button override for the object
  • The prePopulateValues method defined in the action attribute is called before the page is ever rendered
  • The <apex:pageMessages> tag is where any exception/failure messaging will be displayed

The Apex Class is next.

/*
	Created by: Greg Hacic
	Last Update: 9 February 2016 by Greg Hacic
	Questions?: greg@interactiveties.com
	
	NOTES:
		- extension for newOpportunityOverride.page
		- built in order to pre-populate a Name, Stage & Close Date values on new Opportunity records
		- tests located at newOpportunityOverrideTest.cls
*/
public class newOpportunityOverride {
	
	private final Opportunity o; //Opportunity object
	
	//constructor
	public newOpportunityOverride(ApexPages.StandardController standardPageController) {
		o = (Opportunity)standardPageController.getRecord(); //instantiate the standard controller for the Opportunity object
	}
	
	//method called from the Visualforce page action attribute
	public PageReference prepopulateValues() {
		
		Map<String, String> passedParams = System.currentPageReference().getParameters(); //grab the parameters for the current page
		PageReference pageWhereWeEndUp = new PageReference('/006/e'); //set the return page reference to the New Opportunity page
		
		pageWhereWeEndUp.getParameters().putAll(passedParams); //copy all of the mappings from passedParams map to pageWhereWeEndUp map (in case Salesforce sends something we don't know we need)
		
		//if the Account Id was passed
		if (passedParams.containsKey('accid')) { //if the passedParams map contains the key accid
			List<Account> accounts = [SELECT Name FROM Account WHERE Id = :passedParams.get('accid')]; //query for Account.Name
			if (!accounts.isEmpty()) { //if the query resulted in a record
				pageWhereWeEndUp.getParameters().put('opp3', accounts[0].Name+' - '+Date.Today().format()); //pass the Opportunity Name value formatted as the Account.Name - date
			}
		}
			
		pageWhereWeEndUp.getParameters().put('opp11','Prospecting'); //StageName = Prospecting
		pageWhereWeEndUp.getParameters().put('opp9',Date.Today().addDays(30).format()); //Close Date defaulted to 30 days from now
		
		//you may get invalid session errors while trying to automatically save via redirect so we need to remove any auto save keys from the map
		String dropSaveNew = pageWhereWeEndUp.getParameters().remove('save_new'); //remove the save_new key value pair
		String dropSave = pageWhereWeEndUp.getParameters().remove('save'); //remove the save key value pair
		
		pageWhereWeEndUp.getParameters().put('nooverride', '1'); //prevents looping after recordtype selection (if applicable)
		pageWhereWeEndUp.setRedirect(true); //indicate that the redirect should be performed on the client side
		return pageWhereWeEndUp; //send the person on their way
	}

}

Because we may be accessing the Visualforce page and class from a number of different places within Salesforce, we cannot control what information may or may not be passed as the person navigates. Therefore, we need to make sure that we allow for the pass-through of parameters that we didn't expect. This is done on line 26 where we pass all of the key value pairs to the new page reference. We do this early in the logic so that we can overwrite any existing key/value parameters if necessary.

We expect that Salesforce will natively pass an Account Id as the key accid when it is available. But we need to be able to handle situations when the accid key is not passed. Furthermore, we need to make sure that when the key is missing we still get the person to the new Opportunity detail page but just not populate the Name value. This logic is handled in lines 28 - 34 of the Apex class.

Those of you that have been hacking Salesforce URLs for a while may be aware that you used to be able to pass a save or save_new parameter in order to have the application save the record without the person actually clicking the "Save" button. Recently Salesforce started to throw invalid session Id exceptions when trying to auto-save records via URL. For this reason, we want to make sure to strip out those two parameters if that happen to be passed to our Apex class. This is done on lines 39 - 41.

Now that we've got our Visualforce page and Apex class controller we can override the "New" button on the Opportunity object. Navigate to Setup > Customize > Opportunities > Buttons, Links, and Actions. Find the "New" button link in the page and click the "Edit" link. Select the newOpportunityOverride Visualforce page and click the "Save" button.

The final piece to our functionality is the Apex test logic. The unit test below will get you 100.00% code coverage for the Apex Class as I have it written above.

/*
	Created by: Greg Hacic
	Last Update: 9 February 2016 by Greg Hacic
	Questions?: greg@interactiveties.com
	
	Notes:
		- tests newOpportunityOverride.cls (100.00% coverage)
*/
@isTest
private class newOpportunityOverrideTest {
	
	//newOpportunityOverride.cls
	@isTest //defines method for use during testing only
	static void newOpportunityOverride() {
		//BEGIN: setup items
		//create some accounts
		List<Account> accounts = new List<Account>();
		accounts.add(new Account(Name = 'Testing Company'));
		insert accounts;
		//create some contacts
		List<Contact> contacts = new List<Contact>();
		contacts.add(new Contact(AccountId = accounts[0].Id, FirstName = 'Tess', LastName = 'Dachshund'));
		insert contacts;
		//END: setup items
		
		Test.startTest(); //switch to testing context
		
		PageReference pageRef = Page.newOpportunityOverride; //create a page reference to our Visualforce page
		Test.setCurrentPage(pageRef); //set the current page to that page
		ApexPages.StandardController standardController = new ApexPages.standardController(new Opportunity()); //instantiate the standard controller for the Opportunity object
		newOpportunityOverride ext = new newOpportunityOverride(standardController); //construct the extension
		
		//pass some parameters to the page
		pageRef.getParameters().put('save_new', '1'); //set save_new key value pair
		pageRef.getParameters().put('conid', contacts[0].Id); //set conid key value pair
		pageRef.getParameters().put('accid', accounts[0].Id); //set accid key value pair
		pageRef.getParameters().put('save', '1'); //set save key value pair
		
		//execute the logic
		String validationURLString = ext.prepopulateValues().getURL(); //get the resulting url from the prepopulateValues method
		
		//validate the results
		System.assertEquals(true, validationURLString.toLowerCase().contains('nooverride=1')); //string contains the nooverride key value pair
		System.assertEquals(true, validationURLString.contains('conid='+contacts[0].Id)); //string contains the conid key value pair
		System.assertEquals(true, validationURLString.contains('accid='+accounts[0].Id)); //string contains the accid key value pair
		System.assertEquals(true, validationURLString.contains('/006/e')); //string contains the new Opportunity URL
		System.assertEquals(true, validationURLString.contains('opp3')); //string contains the opp3 key
		System.assertEquals(true, validationURLString.contains('opp11')); //string contains the opp11 key
		System.assertEquals(true, validationURLString.contains('opp9')); //string contains the opp9 key
		System.assertEquals(false, validationURLString.toLowerCase().contains('save_new=1')); //string doesn't contain save_new=1
		System.assertEquals(false, validationURLString.toLowerCase().contains('save=1')); //string doesn't contain save=1
		
		Test.stopTest(); //revert back to normal context
	}

}

So there you have it. Another way to pass values to a new record but this time we are using Apex. Additionally, this concept can be applied to any object in your org.

Automated Exchange Rates in Salesforce.com

Reduce Repetitive Tasks, Eliminate Errors & Free Up Your Administrators.

Birthday Reminders for Salesforce.com

It might lead to a sale. Or it might make you feel good.