For auditing purposes, we'd like to lock Opportunities that have been closed for 90 or more days so that they can't be edited anymore, (except perhaps for a super-user profile, but you don't need to address that in your answer).

I know that I could enforce this with the following process, but I'm curious whether there's a simpler or more elegant way to do this:

  1. Create a custom field called "Date First Closed." This field is updated by workflow the first time the opportunity is closed. (Can't use Close Date b/c users can change that).
  2. Validation rule on Opportunity that blocks saving if Date First Close is 90 days ago or more, unless the user is in one of the allowed profiles.


  • I'd prefer to avoid using multiple Record Types, because we already have several different Record Types for different sales processes, and it would be cumbersome to maintain Active and Locked versions of each one.
  • Our default sharing mode is Private, so it would be great to do something with Sharing Rules... but I don't see how I could block the Owner from editing, since Owner always has read/write permissions.

5 Answers 5


I recently wrote a blog post about locking records using approval processes. It is a pretty big hack, but it allows you to lock a record without having to use separate page layouts or record types. Here's how it could work:

  1. Set a time-based workflow rule to update a boolean field called "Lock Record" 90 days after the Opportunity is closed.
  2. Have a trigger that fires when the "Lock Record" field is checked.
  3. The trigger invokes a simple approval process that when finished leaves the record locked.

Admins can unlock the record by clicking the Unlock button at the top of the page, but they can also edit the record without unlocking it.

  • It's worth nothing that with the Winter '16 release coming out, there's additional methods that simplify this process.
    – Nick C
    Aug 29, 2015 at 2:29
  • @NickCook can you add more on the new features?
    – Saariko
    Jan 4, 2016 at 8:34

We have done this using a simple validation rule on the oppty object, this checks if the Close date is 90 days old, and if true on the oppty status [Closed, Closed Won, whatever] - returns an error.

OR(ISPICKVAL(PRIORVALUE(StageName),"Selected - PO Received"), 
ISPICKVAL(PRIORVALUE(StageName),"Closed Won")), 
CloseDate + 90 < TODAY() 

Off course, you can add the profile check as well (we use the administrator profile ID for allowing changes)

NOT ($Profile.Id = "00e20000004456")

Always remember to tune your query as much as possible for better performance.


We did something similar, however we created a specific boolean field for this purpose: "locked__c".

The benefit of using this field instead of a 'first closed date', is it can be used in multiple ways and remains true to its meaning. For example, you may want to lock an Opportunity for a reason other than it was closed 90 days ago, such as based on the Amount value or the Client. By having a generic 'locked' field, the same field is used and it's easy to report on, otherwise you'd have to create multiple fields to achieve the same result.

Our use was slightly different. We wanted certain profiles to have the ability to edit a record in a very small number of situations. We made it possible for those profiles to untick the 'locked' field, then edit it, and workflow would re-tick the field.
We wanted these users to be able to help themselves, but we also wanted to monitor it, so we had emails sent to the sys admins whenever the 'locked' field was unticked for auditing purposes. This approach worked out quite well as the users could perform their tasks, but we could also control it.


The best non-record type solution I can think of is to have a time-based workflow change the owner. You could then use sharing rules to give other users read-only access to the records.

In addition, while extra record types might be cumbersome to maintain they still might prove to be the simplest and cleanest solution in the end.

  • 3
    I think this will be very problematic to reporting. If sales people need to report on their sales, this breaks all the data. Unless you have 2 users for each salesperson, but that's Utopia
    – Saariko
    Sep 3, 2012 at 7:21
  • Very good point on reporting.
    – Mike Chale
    Sep 3, 2012 at 11:48

I have used this syntax in a validation which has seemed to work well.

PRIORVALUE(LastModifiedDate) <> NOW()
  • Work well in what manner? this will always return a TRUE answer, unless I miss something
    – Saariko
    Sep 5, 2012 at 14:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .