0

I have two object foo__c and zoo__c. zoo__c is a child of foo__c with a master detail relationship. I have validation rule created on Zoo__c with a condition that when the status__c field on related Foo__c object is 'Open' then throw an error. => Foo__r.status__c== 'Open'

I wrote a trigger on Foo__c , which creates a new zoo__c record whenever foo__c is updated. Below is my logic in trigger:

trigger footrigger on Foo__c (after insert,after update) {

       if(trigger.isUpdate && trigger.isAfter){
       List<zoo__c> zlist = new List<zoo__c>();
            For(Foo__C f : trigger.new){
              Zoo__c z = new Zoo__c();
              z.Foo__c = f.id;
              z.Name='AZooObj'; 
              zlist.add(z);
            }
        Database.insert(zlist,false);
       }
    }

Note I use Database.Insert to pass the records. Hence the records that meets validation rule on zoo__c should create a new zoo__c record and fail the one which does not meet the rule.

The Problem : When I update a foo__c record from UI which has its status__c field as 'Open', the validation rule does not kick in and it updates the foo__c record without any error. However when I use Insert statement instead of Database.Insert , the validation rule kick in at that time. How can I resolve this situation with Database.Insert and Validation rule. I want to only thrown the error for the records that does not meet the validation rule and pass other records which meets the validation rule. Say I am updating 30 foo__c records using dataloader. If I use Insert statement, it fails all the records which I do not want. I want to fail only the ones which does not meet the validation rule. Any suggestions?

  • 1
    Why don't you only create zoo__c records for those instances that you want them? i.e. check first whether the foo__c record is open before creating the zoo__c record – Nick Cook Aug 10 '17 at 1:14
  • Hmm good idea but I want to fail foo record also if I am unable to create zoo record. I am thinking may be I can put a validation rule of foo to prevent it from updating as well – SfdcBat Aug 10 '17 at 1:26
3

You should use addError to report the error to the parent context:

trigger footrigger on Foo__c (after insert,after update) {

   if(trigger.isUpdate && trigger.isAfter){
   List<zoo__c> zlist = new List<zoo__c>();
        For(Foo__C f : trigger.new){
          Zoo__c z = new Zoo__c();
          z.Foo__c = f.id;
          z.Name='AZooObj'; 
          zlist.add(z);
        }
   Database.SaveResult[] results = Database.insert(zlist,false);
   for(Integer index = 0, size = Trigger.new.size(); index < size; index++) {
     if(!results[index].isSuccess()) {
       Trigger.new[index].addError('Related record failed to insert with error: '+results[index].getErrors()[0].getMessage());
     }
   }
}

By doing this, you will not blow up an entire transaction (e.g. 200 records) when just one fails. This is ideal for when you're using the import wizard, data loader, API calls, etc, because you will be able to identify which record failed and why; the remainder of the records will be able to save independently when the insert does not fail.

You can read more about how the system handles partial failures in Bulk DML Exception Handling.

  • Thank you! My requirement is I do not want to update the foo record if zoo cannot be created. What if I use a validation rule on foo such that it prevents it from updating and it will also help in partial update of foo record( with its zoo record created). Any concern with this approach? – SfdcBat Aug 10 '17 at 2:22
  • 1
    @SfdcBat This process will allow you to update 200 Foo__c records at once. If one of the 200 fail, the remaining 199 will still successfully update. What happens when you "addError" with "allOrNone" set to false is that the system will set aside the error records and retry the remaining records a second time. Read Bulk DML Exception Handling. – sfdcfox Aug 10 '17 at 2:25
2

If you use insert statement to insert records then either all the records will get processed or none of them are processed.

Since you are using Database.insert(list, false) with allorNone as false, so it is not stopping you the processing of insert.

So, after insertion, you can retrieve the errors from SaveResults and display those errors to the users.

trigger footrigger on Foo__c (after insert,after update) 
{
    try
    {
       if(trigger.isUpdate && trigger.isAfter)
       {
        List<zoo__c> zlist = new List<zoo__c>();
            For(Foo__C f : trigger.new){
              Zoo__c z = new Zoo__c();
              z.Foo__c = f.id;
              z.Name='AZooObj'; 
              zlist.add(z);
            }
        Database.SaveResult[] srList = Database.insert(zlist,false);

        List<String> lstErrorString = new List<String>();
        // Iterate through each returned result
            for (Database.SaveResult sr : srList) 
            {
                if (sr.isSuccess()) {
                    // Operation was successful, so get the ID of the record that was processed
                    System.debug('Successfully inserted record, Id: ' + sr.getId());
                }
                else {
                    // Operation failed, so get all errors                
                    for(Database.Error err : sr.getErrors()) 
                    {
                        lstErrorString.add(err.getMessage()); 
                    }
                }
            }
            if(!lstErrorString.isEmpty())
            {
                throw new CustomException(String.join(lstErrorString,','));
            }
       }
    } catch(Exception ex)
    {
        throw new CustomException(ex.getMessage());     
    }
}
  • Would this prevent that specific Foo__c record from updating? – SfdcBat Aug 10 '17 at 1:49
  • no it will not, your validation rule is on zoo__c. Or otherwise, you can insert master and detail in a single transaction based on externalid – Santanu Boral Aug 10 '17 at 1:54
  • According to your question you are not preventing updates on Foo__c record, you are mainly concerned about inserting Zoo__c record and displaying error message – Santanu Boral Aug 10 '17 at 1:56
  • I see your point. May be I did not mention that I do not want to update the foo record if its corresponding zoo record fails. I got an idea to use a validation rule on Foo__c itself which will prevent it from updating that specific record for which zoo cannot be created. With this I do not have to use Database.Insert method as well and Insert should be enough. – SfdcBat Aug 10 '17 at 2:03
  • yes, that should be – Santanu Boral Aug 10 '17 at 2:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.