[SalesForce] Triggers and Custom Validation Rules are in place, how can I make the system to only show Custom Validation Rules’ error message

I have written a few triggers for our business org and have multiple validation rules in place.

I was wondering if there's a way to only show Custom Validation Rule's error message instead of a long list of "nonsense" for our users (like shown below):

"Apex trigger primaryLiaisonCount2 caused an unexpected exception, contact your administrator: primaryLiaisonCount2: execution of AfterUpdate caused by: System.DmlException: Update failed. First exception on row 0 with id 00100000006j9JYAAY; first error: FIELD_CUSTOM_VALIDATION_EXCEPTION, When the Account's Back-up field is set to BUCA, you must make a selection for Back-Up Coverage. If you're editing an Opportunity, please first go to the parent account and select a value for Back-Up Coverage.: [Back_Up_Type__c]: Trigger.primaryLiaisonCount2: line 34, column 1"

What they needed to see is only:
"When the Account's Back-up field is set to BUCA, you must make a selection for Back-Up Coverage. If you're editing an Opportunity, please first go to the parent account and select a value for Back-Up Coverage."

This will help our users to be "less" confused. Any help is greatly appreciated!!

Updated Trigger below

trigger primaryLiaisonCount2 on Contact(after update) {
if(checkRecursive.runOnce()){
    List<Account> accList = new List<account>();
    List<Account> accListToUpdate = new List<account>();
    Set<Account> accSet = new Set<Account>();
    set<id> allAccIds = new Set<id>();    

for (Contact c1: trigger.new){
    //excluding Marketo, Ken Reyes, and Arnold one line below.
    if (c1.AccountID != null && c1.LastModifiedById != '00500000006wnoE' && c1.LastModifiedById != '00500000006xdMm' && c1.LastModifiedById != '00500000006wsIm'){
    allAccIds.add(c1.AccountID);
    } 
}


decimal amount1=[SELECT count() FROM Contact WHERE AccountID IN :allAccIds AND Primary_Liaison__c != null AND Marketing_Status__c != 'Gone From Company'];
decimal amount2=[SELECT count() FROM Contact WHERE AccountID IN :allAccIds AND Receive_Client_Survey__c = true AND Marketing_Status__c != 'Gone From Company'];
LIST<Account> acc1=[SELECT ID FROM Account WHERE ID IN :allAccIds AND recordtypeID = '012000000000j8S'];
list<Account> acc3=[SELECT ID From Account WHERE ID NOT IN :allAccIds AND recordtypeID ='012000000000j8S'];

 for (Contact c1: trigger.new){
    for (Account acc2:acc1){
     acc2.Primary_Liaisons__c = amount1;
     acc2.Receive_Client_Surveys__c = amount2;
     accList.add(acc2);           
     }
 }

   accSet.addall(accList);
   accListToUpdate.addall(accSet);
   Update accListToUpdate;
 }
}

Best Answer

Put your DML statement inside the "Try" section of a Try Catch block and then handle the DML error like this:

List<Custom__c> trgCusts; // this is the list you are trying to perform DML on

try {
    update trgCusts;
}
catch (DMLException e) {
    for (Custom2__c cust2 : trigger.new) {
        cust2.addError(e.getDmlMessage(0));
    }
}
catch (Exception e) {
    for (Custom2__c cust2 : trigger.new) {
        cust2.addError(e.getMessage());
    }
}

Be aware that this will fail every record in the trigger batch with the first record's error message - possibly desirable but if not you can use the Database.update() method to receive detailed information about which records fail and then map these back to the trigger records and only fail those that you want to.

Update

trigger primaryLiaisonCount2 on Contact(after update) {
    if(checkRecursive.runOnce()){
        List<Account> accList = new List<account>();
        List<Account> accListToUpdate = new List<account>();
        Set<Account> accSet = new Set<Account>();
        set<id> allAccIds = new Set<id>();    

        for (Contact c1: trigger.new){
            //excluding Marketo, Ken Reyes, and Arnold one line below.
            if (c1.AccountID != null && c1.LastModifiedById != '00500000006wnoE' && c1.LastModifiedById != '00500000006xdMm' && c1.LastModifiedById != '00500000006wsIm'){
            allAccIds.add(c1.AccountID);
            } 
        }


        decimal amount1=[SELECT count() FROM Contact WHERE AccountID IN :allAccIds AND Primary_Liaison__c != null AND Marketing_Status__c != 'Gone From Company'];
        decimal amount2=[SELECT count() FROM Contact WHERE AccountID IN :allAccIds AND Receive_Client_Survey__c = true AND Marketing_Status__c != 'Gone From Company'];
        LIST<Account> acc1=[SELECT ID FROM Account WHERE ID IN :allAccIds AND recordtypeID = '012000000000j8S'];
        list<Account> acc3=[SELECT ID From Account WHERE ID NOT IN :allAccIds AND recordtypeID ='012000000000j8S'];

        for (Contact c1: trigger.new){
            for (Account acc2:acc1){
                acc2.Primary_Liaisons__c = amount1;
                acc2.Receive_Client_Surveys__c = amount2;
                accList.add(acc2);           
            }
        }

        accSet.addall(accList);
        accListToUpdate.addall(accSet);

        try {
            update accListToUpdate;
        }
        catch (DMLException e) {
            for (Contact con : trigger.new) {
                con.addError(e.getDmlMessage(0));
            }
        }
        catch (Exception e) {
            for (Contact con : trigger.new) {
                con.addError(e.getMessage());
            }
        }
    }

}

The code inside each Catch block will only run if that type of Exception is thrown - so only one Catch block will ever run. That's why the fact they are both called "e" doesn't matter.

When performing DML if an exception occurs (e.g. validation rule) then a DMLException will be thrown which has a special method on it called getDmlMessage(). The other catch block (catch (Exception e)) is optional you can remove it seeing as you are only doing DML in the Try section.

Related Topic