One of the most overlooked configurations I see in orgs is leaving records open to edits that should be locked. For example, sales or support processes can drag on indefinitely if closed cases or opportunities remain editable. Closed records should be closed. Like Chick-fil-A-on-a-Sunday closed.
Locking records does not need to be complicated. You don’t need a new “locked” record type that is updated by a trigger and assigned a page layout with read-only fields. You don’t need a Visualforce page that replaces the standard edit button. And no, you definitely don’t need a validation rule that checks for changes to every single field on the object.
Let’s explore how a record can be locked with a custom permission (optional) and a validation rule with only one or two criteria.
Users at Universal Containers have been adding products to closed won opportunities, rather than creating a new opportunity to track new sales. Additionally, users have been reviving closed lost opportunities for cold prospects that reheat in order to improve their win rate and avoid creating a new record.
Sales leadership would like to prevent these undesired processes since they are distorting a variety of sales metrics. Luckily for UC, their Salesforce admin, Tom, can create validations that rule to lock down closed opportunities.
- Create and assign a custom permission.
- Write opportunity validation rule.
- Review automations.
First, we need to define who can edit closed opportunities.
Rather than hardcoding profile or user parameters, create a custom permission: Modify Closed Opps. Custom permissions should always be used in place of profiles and users in validation rules as they are better to scale, maintain, and deploy.
Assign this custom permission to the system administrator profile and any other profiles that will need to edit these records. The custom permission can also be assigned to a permission set if needed by specific users.
We now have a clean exclusion in our validation rule:
We next need to determine the criteria for this validation rule. For this requirement, simply lock opportunities once they’re closed – won or lost.
Since we still need to allow users to close opportunities, but prevent edits thereafter, we will use the PRIORVALUE function. This function will set a trap that allows records to enter a closed stage but will lock them once they do. Combining this logic with our exclusion above, we get the following error condition formula:
…no more zombie opps. If you have an opportunity process builder with IsClosed = True entry criteria, read-on.
There are three types of automated record updates that can be blocked by this validation that are outlined below. Note that workflow rule field updates, scheduled actions, and account ownership transfers bypass validation rules.
- Process builder immediate action field updates.
Record updates that fire after a record is locked will result in the dreaded FIELD_CUSTOM_VALIDATION_EXCEPTION error. To avoid this, I recommend one of two approaches: (1) use a validation rule to require users to manually update the field in question before the record is locked, or (2) exempt edits to the field(s) within the record lock validation rule with NOT(ISCHANGED([Field in Question]))
- Flow record update elements.
See #1 above.
- Apex updates.
As always, if you have custom code, run Apex tests to verify that the new validation rule does not cause your tests to fail. Run tests early and often – your dev resource will appreciate it.
This simple solution can also be applied to closed cases, activated orders, or any other criteria that should render a record as read-only. If you have a path or process with a defined end, consider this record lock solution to both improve data quality and encourage better business practices.
I’m having a problem with the validation formula including this piece NOT(ISCHANGED([Field in Question])) in order to exempt a field.
Can you help?
What is the custom permission set for “modify closed opportunities”? I’m trying to build it but am unable to determine what the permission set should include. Thanks
Looks like formula field is dynamic and can be easily updated as is referenced to another field or value from formula.