Requiring Fields at a Certain Opportunity Stage

Throughout the sales cycle, there may be certain fields that you would like to required by stage.  There are different ways to do this, for example changing record types by stage and having required fields or using validation rules to require fields if the stage is equal to a certain value.  Each way to do this has its own benefits and also its own negatives.  I had this need come up with a specific caveat, that it should be very easy for someone who is an admin be able to manage it with ease, as well as show users what fields would be required when the stage was selected.  My first thought was to use a Flow that would walk the users through the fields that would be required at that stage and at the end of the Flow, update the stage to the new value.  Although this sounded great, it would require someone to update a Flow to add new fields to the requirement.  Flows are great for requiring fields to be entered through required fields on a screen, but managing a change in a picklist value, with record types, as well as adding or removing required fields as the business dictates, makes a Flow a lot to manage.  Enter another level of Salesforce adminvelopment.  (Yes, I saw on the community someone posted, adminveloper, and adminvelopment is a byproduct of adminveloper.)

I didn’t want to have to use Apex code (test classes are no fun!) but I knew that using Flows wouldn’t be the right answer as a lot of changes to the Flow would be needed, such as adding a new field to the Fast Lookup, adding a new Required field to the Screen element, updating the picklist values of a Dropdown field in the Flow, and writing the appropriate assignment of new or removed fields.  So, I dabbled with Visualforce and a little bit of Javascript. (I promise, you can do this!!!)

I started with the first requirement, quickly and easily add or remove fields that were required by stage.  Field Sets are perfect for this!  I also knew that if a certain stage was selected, it should fail if the required fields weren’t selected.  Although Validation Rules are a great way to do this, it’s not very intuitive for the User, and they might save the record a few times and see the Validation Rule message each time until they have all the required fields filled in.  Although it works, it is not very User friendly.  So what is an Awesome Admin to do?!  Enter a hidden field, a Visualforce Page, and a little bit of Javascript!  I know, the answer isn’t a Flow.  I know, I’m a Flownatic.  I know, I’m not a developer.  But that’s ok, because this solution is perfect for an Adminveloper and helps a business to continue to develop their business flow!

First, let’s start with a simple field.  Create a field, visible from a field level security perspective, but hidden from a page layout.  In this case, we’ll call it “Closed Won Required Fields”.  It should be a checkbox field, visible to all profiles, that is hidden from a page layout.  Next, we’ll create a validation rule that says, “[Closed_Won_Required_Fields__c = False && TEXT(StageName) = “Closed Won”].  This will prevent a User from setting the Stage to Closed Won unless Closed_Won_Required_Fields__c is True.  Since the User can’t click the box (not visible in the page layout), the User will have to follow the required process.  For the Error Message of the Validation Rule write, [You can only set an Opportunity as Closed Won by clicking the Closed Won button.] and set it at the “Top of the Page”.  This way, if a User tries to set the Stage to Closed Won, it will fire the Validation Rule and let the User know they must click a button, “Closed Won”, to actually save the record as Closed Won.

Opportunity Validation Rule

This is where the fun begins!

First, create a new field set.  In this case, go to Setup, Customize, Opportunities, Field Sets.  Click New, set the name to “Closed Won Required Fields”, and enter information into the “Where is this used?” section, such as, “These fields will be required for Closed Won.”.  Click Save.  Next, add in the fields you would like into the Field Set, in this example, we’ll use Amount, Description, and Type.

Field Set

Click Save.

Now that we have our Field Set, lets create a new Visualforce Page.  Go to Setup, Develop, Visualforce Pages.  Click New.  Set the page name as “Required Closed Won Fields”  and make sure to change the spaces in the “name” section to “_” so that the Label reads “Required Closed Won Fields” and the Name reads “Required_Closed_Won_Fields”.

Required Closed Won Fields Start

Next, enter the following information (Sorry I couldn’t input the code directly, wordpress was giving me issues!):

Visualforce Page Required Fields Closed Won

<!-- Main information for the page including the "standardController" for the object (in this case, Lead) -->
<apex:page standardController="Lead" sidebar="true" showHeader="true" showChat="false" tabStyle="Lead" id="mainPage">
<!-- Show any error messages or validation rules -->
<apex:pageMessages ></apex:pageMessages>
<!--Start of the form -->
<apex:form id="mainForm" >
<!-- Showing the page block "standard" look and feel -->
<apex:pageBlock title="Required Fields for Lead Convert to Opportunity" id="mainBlock">
<!-- Start of the section -->
<apex:pageBlockSection columns="2" id="mainSection">
<!-- The Stage field and a call to the Javascript -->
<apex:inputField value="{!Lead.Status}" rendered="true" id="status" onchange="checkStatus();"/>
<!-- The values in the field set -->
<apex:repeat value="{!$ObjectType.Lead.FieldSets.RequiredConvertFields}" var="field">
<!-- Displaying the values through required input fields -->
<apex:inputField value="{!Lead[field]}" styleClass="allFields" />
<!-- Closing the field set fields -->
</apex:repeat>
<!-- Adding the hidden field with CSS to not "display" the field in the page, even though the Javascript can "see" the field -->
<apex:inputField label="" id="cL" style="display:none;" value="{!Lead.ConvertRequired__c}"/>
<!-- Closing the page block section -->
</apex:pageBlockSection>
<!-- Start the page "buttons" from a normal page -->
<apex:pageBlockButtons >
<!-- Set the Save button -->
<apex:commandButton value="Convert" action="{!quicksave}" oncomplete="complete();" />
<!-- Set the Cancel button -->
<apex:commandButton action="{!cancel}" value="Cancel"/>
<!-- Close the button section -->
</apex:pageBlockButtons>
<!-- Close the page block -->
</apex:pageBlock>
<!-- Close the form -->
</apex:form>
<!-- Start the Javascript checking the status for Qualified and updating it "onchange" -->
<script>
function checkStatus(){
var s = document.getElementById("mainPage:mainForm:mainBlock:mainSection:status").value;
if(s == "Qualified"){
document.getElementById("mainPage:mainForm:mainBlock:mainSection:cL").checked = true;
}
else {
document.getElementById("mainPage:mainForm:mainBlock:mainSection:cL").checked = false;
}
}
function complete(){
var s = document.getElementById("mainPage:mainForm:mainBlock:mainSection:status").value;
var r = true;
var reqs = document.getElementsByClassName('allFields');
for(var i = 0; i < reqs.length; i++) {
if(!reqs[i].value){
r = false;
}
}
if((document.getElementById("mainPage:mainForm:mainBlock:mainSection:status").value == '' || r == false) && s == 'Qualified' ){
alert('All fields are required to Convert as a Qualified Lead!');
} else if(s == "Qualified"){
window.location='/lead/leadconvert.jsp?retURL={!Lead.Id}&id={!Lead.Id}&cstatus=5';
} else if(s == "Closed - Converted"){
window.location='/lead/leadconvert.jsp?retURL={!Lead.Id}&id={!Lead.Id}&nooppti=1&cstatus=1';
} else {
window.location='/{!Lead.Id}';
}
}
</script>
<!-- Close the Apex Page -->
</apex:page>

The comments (everything between ) should explain what the page is doing, but to recap: The page is setting the Javascript function when the stage is changed, setting the hidden field to true if the stage is “Closed Won” and false if it is not.  This allows for the Validation Rule to be true if the User selects “Closed Won” since it will check the box and all the fields on the page will be required.  These are the same fields in the Field Set and can dynamically be added or removed through the field set without issue to the code.

Click Save.  Make sure to set the security settings so that every profile can see the page!

Now, let’s create a button.  Go to Setup, Customize, Opportunities, “Buttons, Links, and Actions”.  Click “New Button or Link”.  Set the name as “Closed Won”, the Display Type as Detail Page Button, Behavior as “Display in existing window without sidebar or header”, Content Source as “URL”,  and the formula to be “{!URLFOR(“/apex/Required_Closed_Won_Fields?id=”&Opportunity.Id,Opportunity.Id)}”.

Required Closed Won Fields Button

Click Save.  Add the button to your Page Layout.

Now, if your User tries to save the Opportunity as Closed Won through the normal page, they will receive an error, “You can only set an Opportunity as Closed Won by clicking the Closed Won button.”.  If they click the Closed Won button, it will take them to the Visualforce page with the fields that are required for setting the stage to Closed Won.  When the User moves the stage to “Closed Won” the Javascript will update the hidden field to True which will pass the validation rule and if the user changes the field from Closed Won to something else, it will set the field to False.

Any Admin can update the Field Set to include or remove fields that should be part of the required fields to save the Opportunity as “Closed Won”.

This is your full solution, requiring no Apex coding or test classes, to have a User be required to enter information in a certain stage, in this case “Closed Won”, in order to save a record.  Adminveloper for the win!!!

Let me know how this is working for you in the comments!

10 thoughts on “Requiring Fields at a Certain Opportunity Stage

  1. Hi there! I am getting the following errors when trying to save the VF page. Any chance you can help me out?
    The value of attribute “standardController” associated with an element type “apex:page” must not contain the ‘<' character
    The value of attribute "standardController" associated with an element type "apex:page" must not contain the '<' character.

    Here is my code:

    <apex:inputField value=”{!Opportunity.StageName}” rendered=”true” required=”true” id=stageName onchange=”checkStage();”

    function checkStage(){
    var s = document.getElementById(“mainPage:mainForm:mainBlock:mainSection:stageName”).value;
    if(s == “Qualification Confirmed”){
    document.getElementById(“mainPage:mainForm.MainSection:cWRF”).checked = true;
    }
    else{
    document.getElementById(“mainPage:mainForm.MainSection:cWRF”).checked = false;
    }
    }

    Like

    • Sorry, it didn’t copy all of my code. Here it is:

      <apex:inputField value=”{!Opportunity.StageName}” rendered=”true” required=”true” id=stageName onchange=”checkStage();”

      function checkStage(){
      var s = document.getElementById(“mainPage:mainForm:mainBlock:mainSection:stageName”).value;
      if(s == “Qualification Confirmed”){
      document.getElementById(“mainPage:mainForm.MainSection:cWRF”).checked = true;
      }
      else{
      document.getElementById(“mainPage:mainForm.MainSection:cWRF”).checked = false;
      }
      }

      Like

    • Hi Vicki,

      I haven’t tested this in Lightning. It is using the old navigation, so it is possible that it will not work. Lightning also has a new Lead Convert screen, so if it did work, you would lose that Lead Convert screen.

      There are a couple of other things that you could do, however. Lightning does have the ability to have conditional rendering for a component, so if you added a record update quick action in Lightning and rendered it if all the fields were not complete, that would work. You could also add a rich text component to be able to alert the user that in order to convert the lead, the fields “below” need to be filled out. If that is also rendering only if those fields are blank, then that would give the users a nice visual. You could finish it with a Validation Rule on conversion so that if they try to convert it will not work. Although the Validation rule is after the fact and not great, since you have the conditionally rendered rich text area/quick action update, it isn’t as bad of a User experience. Take a look at this post to understand the conditionally rendered piece, it should help if you’re not familiar. https://englhardconsulting.com/2018/06/25/5-reasons-to-switch-to-lightning

      Let me know what you do and how it works, I’m sure others will be interested as well!

      Like

  2. Hey this is exactly what I need!
    But I am receiving this error. The problem I am finding is that I have two options for Closed Won so not sure where to input these on the validation rule formula.
    “Error: Field StageName is a picklist field. Picklist fields are only supported in certain functions.”
    Any help is greatly appreciated.
    Thanks,
    Ross

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.