This framework centralizes all Opportunity stage-based validation rules in a Custom Metadata Type (CMT), providing an efficient, scalable, and easily maintainable way to manage validation logic. The implementation will leverage Before-Save Flows for performance and utilize Apex to enforce these rules dynamically within the code.

While you could use this approach to handle ALL validations on an object, that may become difficult to manage. This approach is intended to be used to validate data entry based on a field universally used on every record of a particular object, like Opportunity Stage or Case Status. We'll use the Opportunity Stage as the example for the rest of this article, in other words: all validations that rely on Opportunity Stage will be consolidated into this framework.


Framework Overview

Goals

  1. Centralize stage-based validation rules in a Custom Metadata Table.
  2. Use a Before-Save Flow to trigger validation logic.
  3. Enforce validation errors directly in Apex, using addError() methods for dynamic error handling.
  4. Support validation for multiple fields, error messages, and error locations.

Key Components

1. Custom Metadata Type: Opportunity_Validation_Rules__mdt

The CMT will store all stage-based validation rules. Each record in the table represents a validation rule.

Fields for Opportunity_Validation_Rules__mdt:

Field NameTypeDescription
Opportunity StagePicklistThe stage for which the validation rule applies (e.g., "Negotiation").
Required FieldsText (255)Comma-separated list of required fields (e.g., CloseDate, Amount).
Validation ErrorText (255)The error message to display if validation fails.
Error LocationPicklistWhere to display the error (Top of Page or Field).
ActiveCheckboxWhether the rule is active (for easier maintenance).

2. Apex Class: OpportunityValidationService

The Apex class will handle dynamic validation logic by querying the CMT and enforcing the rules for the Opportunity using addError() methods.

Key Responsibilities:

  1. Query all active validation rules for the current stage.
  2. Check if the required fields specified in the CMT are populated.
  3. Enforce validation errors dynamically using addError().

Updated Apex Code:

public with sharing class OpportunityValidationService {
    public static void validateOpportunity(List<Opportunity> opportunities) {
        // Query active validation rules
        Map<String, List<Opportunity_Validation_Rules__mdt>> stageToRulesMap = new Map<String, List<Opportunity_Validation_Rules__mdt>>();
        for (Opportunity_Validation_Rules__mdt rule : [
            SELECT Opportunity_Stage__c, Required_Fields__c, Validation_Error__c, Error_Location__c
            FROM Opportunity_Validation_Rules__mdt
            WHERE Active__c = true
        ]) {
            if (!stageToRulesMap.containsKey(rule.Opportunity_Stage__c)) {
                stageToRulesMap.put(rule.Opportunity_Stage__c, new List<Opportunity_Validation_Rules__mdt>());
            }
            stageToRulesMap.get(rule.Opportunity_Stage__c).add(rule);
        }

        // Validate each Opportunity
        for (Opportunity opp : opportunities) {
            List<Opportunity_Validation_Rules__mdt> rules = stageToRulesMap.get(opp.StageName);
            if (rules == null) continue; // No rules for this stage

            for (Opportunity_Validation_Rules__mdt rule : rules) {
                List<String> requiredFields = rule.Required_Fields__c.split(',');
                for (String field : requiredFields) {
                    field = field.trim();
                    if (String.isEmpty((String)opp.get(field))) {
                        if (rule.Error_Location__c == 'Field') {
                            opp.addError(field, rule.Validation_Error__c);
                        } else {
                            opp.addError(rule.Validation_Error__c);
                        }
                    }
                }
            }
        }
    }
}

3. Flow: Validate Opportunity Before Save

A Before-Save Flow will trigger whenever an Opportunity is created or updated.

Steps:

  1. Trigger Flow on Opportunity:
    • Object: Opportunity
    • Trigger: Before Save
  2. Call Apex:
    • Invoke the OpportunityValidationService.validateOpportunity method.
  3. Error Handling:
    • Errors applied through addError() will automatically display in the UI.

4. Data in the Custom Metadata Table

Here’s how the data might look in Opportunity_Validation_Rules__mdt:

Opportunity StageRequired FieldsValidation ErrorError LocationActive
QualificationBudget__cBudget is required.FieldTrue
ProposalEstimated_Launch_Date__cEstimated Launch Date is required.Top of PageTrue
NegotiationDiscount_Percentage__cDiscount must be specified.FieldTrue

Advantages of This Framework

  1. Centralized Management:
    • All stage-based validations are managed in a single table, making it easier to maintain and update rules.
  2. Flexibility:
    • Validation rules can be updated without modifying Flows or Apex. Changes to the CMT are instantly reflected in the logic.
  3. Reusability:
    • The Apex service can handle any Opportunity validation logic tied to stages, reducing duplication.
  4. Scalability:
    • Adding new rules for stages or fields requires only new records in the CMT.
  5. Performance:
    • Using a Before-Save Flow ensures efficient processing by avoiding DML operations.
  6. Dynamic Errors for Multi-Field Validations:
    1. Able to be used to validate groups of fields required at the same time to streamline the user experience when hitting errors. The error message itself could also be made dynamic based on if some of a group of fields are missing while others are provided, to tailor error messages to a specific transaction.

Challenges to Consider

  1. Initial Setup Complexity:
    • Building the CMT, Apex, and Flow requires upfront effort and planning.
  2. UX/UI:
    • Clear handling of field-specific vs. page-level errors is essential for user understanding.

Conclusion

This framework is a scalable, maintainable, and powerful solution for managing Opportunity stage-based validations. By centralizing rules in Custom Metadata Types and using Apex to dynamically enforce validations, you ensure data quality and process flexibility.

This methodology is ideal for Salesforce architects and admins seeking to streamline complex validation requirements while maintaining scalability and simplicity.

Tagged in: