Bypassing Salesforce data validation in rules and APEX triggers for data import & migration
Salesforce provides many ways of ensuring data consistency such as validation rules and APEX triggers.
However, when migrating existing data to Salesforce, usually two contradictory requirements come up:
- Ensure that the data is valid — business analysts and developers define and implement rules and checks to ensure data quality for the future
- Import data quickly — which assumes bringing in data that may be incomplete or invalid according to the new validation rules
The ideal solution is to clean the data before importing; however, this is rarely feasible because:
- It may require considerable effort and time
- Some data points may not be available in source data
- It may be easier (and more cost-effective) to perform the data cleanup in Salesforce
Also, in many projects data migration is iterative (i.e., with ever-improving migration rules and using cleaned-up sources) and sometimes extends beyond the go-live date.
Therefore, there is a need to bypass validation for data migration while keeping it enabled for users at the same time.
This post describes how to solve the problem.
Validation rules
Validation rules are stored with objects (technically, they are a part of object metadata). They can be individually disabled and enabled in the Object Manager:
However, manual management of validation rules is cumbersome as there can be multiple validation rules per multiple object types.
There are tools that enable doing this in bulk (e.g., sftooklit.co) by modifying and changing object metadata but that still involves human intervention (besides, deploying metadata is not a trivial operation).
A better solution is to designate a profile for which validation rules are always disabled and use the following logic:
$Profile.Name <> 'Data Migration Profile' && ( ... original validation logic ... )
For Data Migration Profile the first line always evaluates to false, and the following conditions are not evaluated — so the validation rule is effectively disabled for any user with the profile (rules are executed in the context of the user who triggered operation on an object)..
The same can be achieved with a custom permission if multiple users with different profiles need to be migrating data:
!$Permission.Data_Migration /* <-- developer name of a permission */ && ( ... original validation logic ... )
One of the challenges with validation rules is that organizations overdo them, i.e., define very strict rules that turn out to be impractical. The best way of approaching this is defining a minimum viable rule set and add additional ones on an as needed basis. |
Lookup filters
The same logic can be applied to lookup filters, by adding an OR operation between current lookup filter conditions and checking for data migration:
(Current filter conditions) OR (Data migration)
E.g.:
(Product: Active EQUALS True) OR (Current User Profile: Name EQUALS Data Migration Profile)
This change will not affect which items are selected on the layouts for regular users.
APEX triggers
Although it is not their primary function, APEX triggers can also perform validation. Just like validation rules, they cannot be disabled globally, only individually (also there can be multiple triggers per object):
One option is to just disable triggers altogether — it is generally a good practice to have an option to disable triggers in a package, i.e., a well written package will have an option looking similar to this:
Disabling triggers, however, has potentially even more negative ramifications than disabling validation rules, as they usually perform more complex tasks than just validation.
The best way is to apply the same approach as with validation rules (APEX trigger is executed in the context of a calling user) and skip validation parts. This will be more complicated than validation rules because some logic may have to be executed regardless.
Again, just like with validation rules, this can be done with a profile (error checking omitted for clarity):
Id profileId = UserInfo.getProfileId(); Profile p = [SELECT Id, Name FROM Profile WHERE Id=:profileId]; If(p.Name <> 'Data Migration Profile'){ ... }
Or with a custom permission (error checking omitted for clarity):
String permissionName = 'Data Migration'; // permission name visible in the UI Boolean hasPermission = false; CustomPermission customPermission = [SELECT Id, DeveloperName FROM CustomPermission WHERE MasterLabel =: permissionName]; hasPermission = FatureManagement.checkPermission(customPermission.DeveloperName); // E.g. Data_Migration if( hasPermission == false ) { ... }
Whichever method is chosen it is a good practice to place the logic in a static helper method, e.g., DataHelper.InDataMigration(), to be able to make future changes in a single spot.
Important: Disabling trigger processing for migrated data usually goes beyond just data validation — for example, it may be equally important do suppress email notifications (esp. to customers).
|
Looking at records
The examples above demonstrate bypassing of validation (and other) processing logic based on user properties (profile, login, specific permission, etc.).
Sometimes a complementary strategy of checking if a particular record is/was migrated may also come handy (e.g., suppress email notifications for migrated records).
In that case, we recommend creating a formula field Is_Migrated to flag records that came from migration (typically they will just check external ids used as migration keys for being NOT NULL). This flag may have other uses such as distinguishing between migrated vs. Salesforce-originated records in list views, reports, etc.
Conclusions
It is a good practice to build a bypass of validation rules/triggers into any Salesforce solution that requires legacy data import. This can be achieved in many ways (profiles, custom permissions, designated users, or a combination of thereof) and is best started at the beginning of the project.
Nextian is a vendor of Quote-to-Cash (QTC) software for cloud and communications helping providers accelerate growth and increase customer lifetime value.
Contact us today to find out how we can help you!