This pattern will help you write dynamic validation rules to handle multi-country behavior. It also helps to activate or deactivate the rule based on Profile or Permission Set assigned to user.
Naming Pattern
Component | Validation Rule |
Name | ObjectName_VRXXX_Word1Word2 Ex: Account_VR001_StreetIsMandatory |
Description | Free text Ex: The field Street must contain a value. |
Error Message | Free text Ex: Please fill the Street field. |
Step 1
Start creating a Custom Metadata Type that we will call “ValidationRules”. The reason behind is that it helps configuration to be deployed easily between orgs which is not the case of custom settings records that need to be handle manually. In the past we were adding this kind of information in the user Object but it doesn’t give the flexibility we have with this new settings implementation.
Alternatively, you can move this to Custom setting (hierarchical or not) but it implies more steps and more hard-coding from my point of view.
Step 2
Create a Text Field with length of 255 that we will call “Countries”. Picklist or Multi-Picklist are currently not supported in Custom Metadata types.
Step 3
Populate one record for our first validation rule that we are going to write.
Label | Account validation rule 001 |
Name | Account_VR001 |
Countries | FR;GB |
If your rule is meant to be applied to all countries then you can leave the field blank or add a default value ‘*’ for example.
Use a separator like ‘;’ between each country.
Step 4
Create 2 Custom Permissions to handle activation and deactivation of the rule.
Permission 1 | Permission 2 | |
Name | BP_ACC_VR001 | BP_ALL_VR |
The first one will be used to deactivate all validation rules when the Custom Permission is assigned Profile or Permission Set.
The second one will be used to deactivate only this validation rule.
Step 5
We are going to write a simple validation rule for the Account object based on a standard field “BillingCountry”, we suppose this field will contain value like “FR”, “GB”, etc…
Name | Account_VR001_POC |
Description | This a POC. |
Active | Checked |
Error Location | Top of Page or Field checked Choose Field for better user experience |
Formula | Below |
IF(
NOT($Permission.BP_ALL_VR) &&
NOT($Permission.BP_ACC_VR001) &&
(
CONTAINS($CustomMetadata.ValidationRules__mdt.Account_VR001.Countries__c,'*') ||
CONTAINS($CustomMetadata.ValidationRules__mdt.Account_VR001.Countries__c,BillingCountry)
),
true,
false
)
Now you can save your formula and play with some records and profiles / permission sets to see the magic.
Another thing to retain that I’ve discovered recently is that every time you update the API name of the record of the Custom Metadata Type, it updates automatically the reference in your validation rule! Fantastic !
Step 6
Now that you have mastered the pattern, let’s go further. Imagine you were asked to write a specific rule for France and general rule for all other countries, you will start writing some “IF” condition to handle this.
IF(
BillingCountry == 'FR', doSomething
doOtherThing
)
As number of specificity grows, your rule will become complex to handle.
IF(
BillingCountry == 'FR', doSomething,
IF(
BillingCounbtry == 'GB', doSomething,
doOtherThing
)
)
Fortunately, we can rewrite this formula with the CASE function.
CASE (BillingCountry,
'FR', doSomething,
'GB', doSomething,
doOtherThing
)
Step 7
Sometime you have to write the same rule or same regexp for example for many fields that need validation, to avoid this you can consider using Custom Metadata Type again.
CASE(BillingCountry,
'FR', IF(1==1, 1, 0),
'GB', IF(2==2, 1, 0),
1
) > 0,
Here we can replace a small part of the condition in order to reuse it elsewhere.
CASE(BillingCountry,
'FR', IF(1==1, VALUE($CustomMetadata.Rules__mdt.FR_R1.Value__c), 0),
'GB', IF(2==2, VALUE($CustomMetadata.Rules__mdt.GB_R1.Value__c), 0),
1
) > 0,
For this example, I have created a Custom Metadata Type called “Rules” with a custom Text(255) field named “Value__c”, and I have created one record per country to hold specific values to render.
The validation should run & block if :
- BillingCountry is GB or FR because only those are allowed for this rule
- The user has no global or local bypass assigned
- The result is greater than 0
Conclusion
Keep in mind that there are many ways to implement Validation Rule, but you have to design it well first in order to be scalable and maintainable. Some limitations introduce constraints (Number of characters, number of countries …) that can be challenging and make you rethink the initial design. Try to keep it simple, optimize the rule to reduce complexity and length, mutualize to reduce the number of rule and don’t forget to challenge your business 🙂
Hope you enjoy reading this article, see you soon for the next one ...