function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion

Opportunity Stages: Limited Value Access & Making Sure Each Opportunity Stage is Used


We're trying to implement a 10-stage Opportunity process modeled by 10 Opportunity Stage values that trigger various Workflow Rule Email Alert notifications to sales, legal, and fulfillment reps and managers. 

Our two challenges are:

1) Only allowing reps (users with Profile = "Profile A") to access use certain Opportunity Stages and not others

Some of our Opportunity Stage values reflect manager approvals and should never be selected by reps.  We tried implementing Approvals to faciliate only managers moving Opps to these Stage values, but have found the Approvals feature to be clunky.  Also, there doesn't seem to be a way to have an Approval record be submitted when a rep selects the Opportunity Stage that proceeds the approval step - rather, the Approvals feature only seems to allow for manual submission of approvals via the Approvals related list.  We can't figure out how to assure that Approvals are always submitted and/or assure that Opportunity Stage values reflecting approval steps are selected prior to post-approval Opportunity Stage values being selected.

2) Making sure that each Opportunity Stage is reached individually before Closed/Won or Closed/Lost

Since each of our Opportunity Stage values denotes important internal steps to fulfill an Opportunity in our organization, we need our reps to step through each Stage value in sequence.  We can't figure out a way to avoid allowing reps to bypass Stage values.

Any help greatly appreciated.

Could you keep a hidden field called "previous Stage", and an in-line s-control on Opportunity which always checks to see that the current Stage is equal to the previous Stage. 

If it isn't, then if the current user is one who is "allowed" to update that particular stage, and if the current stage is equal to "previous Stage plus one", then update the previous stage field to the new stage and continue.  (This would be the only way "previous Stage" gets bumped.

If the user isn't allowed to update it, then reset the current stage to the previous stage value and refresh the page.

If the value that they've updated it to isn't "previous plus one" (e.g. they tried to skip a stage, or go backwards), then put up and alert box and redirects the user to the Edit screen for that opportunity.  (Or just reset it as if they weren't allowed to update it.)

This is sort of a post-change validation that runs all the time.  Note that this is ugly and slow.  If you have Apex you could do it cleaner as a post-change trigger.  You're probably better off trying to use the built in approvals if possible.

Upfront Disclaimer: Not much of a solution

As a Admin I have a very similar problem.  We too would like to make only certain stage values available to certain Users (i.e. we don't want our sales reps approving their own deals).  However, we're not as concerned about stages getting skipped because we have several that often do get skipped. 

I've tried many different solutions and the only one I found that came close was to create a validation rule that basically said that if the user profile = x and the stage values didn't equal a, b or c then the record couldn't be saved. 

The obvious problems with this are that you have to create several validation rules for each role or profile and if the stages have progressed past the role of the sales rep then the sales rep can no longer make updates to the record if needed. 

This didn't quite work for us (for other reasons as well) but maybe it will work for you.  If you are able to find a good  solution I'd surely appreciate a heads up.
Another solution is to write an in-line s-control called "Edit Stage" which you place on the Opportunity Page Layout with the label "Stage". 

This will: display the current stage, also display a little "edit" tag.  When the user clicks on the edit tag (not the Salesforce Edit button which brings you to the edit screen), the text changes to a pull-down list of the options that are appropriate for that user's profile.

After the user has selected their choice and moved to another field, the select box reverts to the new Stage value, and the record is updated with the new Stage value.

If you implement this, and don't display the Salesforce "Stage" variable on the overview layout, then you're all set.   You determine what values are visible for what profiles by editing the s-control.  Very easy.

I have code for this if you like.  Steve.

P.S. Note that this doesn't stop the user from going into the Edit screen and changing the Stage there.  So, you'd have to adjust page layouts, etc. to disallow that while still granting the user permission to update the field.

Hi Steve,

I'd love to see your code for that solution.

I'm a newbie Group Edition SalesForce user.  I'm manually entering opportunity data from another CRM and I need to be able to "backdate" the stage date.  Once I enter data into SF, the dates for each stage end up being the same.  Would your solution help me with this?  Thanks.

With enough OR functions, you can make the following fit. This just keeps non-Adminstrators from modifying any Closed/Won deal. Of course, this is easy it is only keeps non-Admins out of Closed Won. The more stages you have, the more rules you might need. I guess you could make a nasty function with enoguh AND and OR to cover all bases, but extra validation rules would probably be easier at that point.



AND( $User.ProfileId  <> "00e50000000n5xV",  ISPICKVAL(StageName, "Closed Won" ))







I would like to see the Code.






This is an s-Control from over two years ago... I'd certainly do it differently now.   However... I found it easily enough so here it is.    Again, let me stress, don't do it this way.  -Steve



    <script type="text/javascript" src="/js/functions.js"></script>
    <script src="/soap/ajax/10.0/connection.js"></script>

<style type="text/css"> 
	body {
		font-family: 'Arial','Helvetica',sans-serif;
		font-size: 75%;
		text-align: left;
	.btn {
	    font-family: 'Verdana', 'Geneva', sans-serif;
	    background-image:  url("/img/bgButton.gif");
	    background-repeat: repeat-x;
	    background-position: left top;
	    border-right:1px solid #5C5D61;
	    border-bottom:1px solid #5C5D61;
	    font-size: 80%;
	    padding:1px 3px;

<script type="text/javascript">

// ChangeLog
// 9/27/2007	Initial.  SBower.

var selList;
var el;
function start() {

	// Edit the select lists that you want for the various Profiles.

	switch("{!$Profile.Name}") {

	case "System Administrator":
  		selList = ["Won", "Won Big", "Administrators always Win"];

	case "Peon":
  		selList = ["Won, Rare", "Lost", "Lost Big", "Total Loss", "Client killed Salesman"];

  		selList = ["Closed", "Won", "Lost", "Undecided", "Apathetic"];


	// Now build a Select tag for the selection list.
	var t = '<select id="newStage" name="newStage" title="Stage" onblur="change()">';
	for (var i = 0; i < selList.length; i++)
		t = t + '<option value="' + selList[i] + '">' + selList[i] + '</option>';
	t = t + '</select>';

	// load the div with the selection.
	el = document.getElementById("stage");
	el.innerHTML = t;

function change() {
	// Get the Value that we've selected
	var t = selList[document.getElementById("newStage").selectedIndex];
	// Update the Stage field in the record (which is not displayed so we don't do a page refresh)
	var upOpp = new sforce.SObject("Opportunity");
	upOpp.Id = "{!Opportunity.Id}";
	upOpp.StageName = t;
	var res = sforce.connection.update([upOpp]);
	// And replace the select with the inner Div to get us back to the starting point.
	el.innerHTML = 	'<div id="bootstrap_div">' + t + 
		'&nbsp;<input type="button" class="btn" value="Edit Stage" onclick="start()"></div>';	

<div id="stage">
	<div id="bootstrap_div" onclick="start()">
		&nbsp;<input type="button" class="btn" value="Edit Stage" onclick="start()">



Carolina Vasques 16Carolina Vasques 16

I'd like a help, please!!. 
I have the approval process on the object case that goes through vários stages and at each stage there is a different group of analysts who can have a different access at each stage. How would you solve this? With a validation rule or just in code?

DraftEdit, view, and deleteYou can't see called sonEdit, view, and deleteEdit, view, and deleteShowEdit, view, and deleteView
CheckShowEdit, viewEdit, preview, and cancelEdit, preview, and cancelShowShowView
TypingShowEdit, viewEdit, preview, and cancelEdit, preview, and cancelShowShowView
RestrictionsShowShowEdit, preview, and cancelEdit, preview, and cancelShowShowView
QuantitativeYou can't see called sonShowEdit, preview, and cancelEdit, preview, and cancelEdit, preview, and cancelYou can't see called sonView
QualitativeYou can't see called sonShowEdit, preview, and cancelEdit, preview, and cancelEdit, preview, and cancelYou can't see called sonView
Validate limitYou can't see called sonShowEdit, preview, and cancelEdit, preview, and cancelEdit, preview, and cancelYou can't see called sonView