0

I am trying to run validation in before update trigger which checks if a field value has changed in a particular status for the record. If it has changed then it adds a validation error.

I wanted to build it dynamically in Apex where I do not have to add field details in trigger everytime a new field is added to the record.

I am using the following piece of code:

  public static void validationOnBookingUpdateWhenCaseIsDelayed(Map<Id, Booking__c> bookingOldMap, List<Booking__c> bookingNewList){
    Map<String,Schema.SObjectField> mfields = Schema.getGlobalDescribe().get('Booking__c').getDescribe().fields.getMap();
    for(Booking__c bookingNewRecord : bookingNewList){
        for(Schema.SObjectField sField : mfields.values()){
            if(String.valueOf(sField) != 'Booking_Status__c'){
                if((bookingNewRecord.get('Booking_Status__c') == 'Delayed' || bookingNewRecord.get('Booking_Status__c') == 'Discontinued') &&
                   bookingNewRecord.get(sField) != bookingOldMap.get(bookingNewRecord.Id).get(sField)){
                       bookingNewRecord.addError('Cannot change');
                   }                    
            }   
            
        }
    }
}

My issue here is that, I am not getting a way to do it without the nested for loop. Any advice would be really helpful.

1 Answer 1

2

Yes, there's a way to compare all fields at once, but if you want to include which fields are changed, a nested loop is your only option. If you only want to detect that any field has changed, you can do that by comparing the two records directly:

for(Booking__c bookingNewRecord : bookingNewList){
    if(bookingNewRecord.Booking_Status__c != 'Delayed' && bookingNewRecord.Booking_Status__c != 'Discontinued') {
        continue;
    }
    // Clear out audit fields, etc
    Booking__c newValues = bookingNewRecord.clone(false, false, false, false);
    Booking__c oldValues = bookingOldMap.get(bookingNewRecord.Id).clone(false, false, false, false);
    // Set both clones to null so it will work in the next step
    newValues.Booking_Status__c = oldValues.Booking_Status__c = null;
    // Comparing two sobjects compares all their contents
    if(oldValues != newValues) {
        bookingNewRecord.addError('Cannot change');
    }
}

This makes it trivial to figure out if you need the error. However, if you want to tell the user which fields have changed, you'd still need an inner for loop.

1
  • Thank you so much. I don't need to have the list of fields that have been edited. I just need to show a message that no values can be changed. This is a great piece of logic. Thank you for explaining it so well. @sfdcfox
    – Madhurima
    Commented Oct 18, 2022 at 19:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .