0

working on this trigger for 2 custom objects in a master detail relationship. the insert part works perfectly, just am struggling with the update portion. i've included the entire code in case my previous map needs to be used. basically, i'm trying to change the type custom field to null if when UPDATING the end date field is set to a value (no longer null).

trigger RoleHistoryAfterTrigger on Role_History__c (after update, after insert) {

Map<Id, Rep__c> parentReps = new Map<Id, Rep__c>();
List<Id> listIds = new List<Id>();

if(Trigger.isInsert) { 
    for(Role_History__c roles :Trigger.New) {
        listIds.add(roles.Rep__c);
    }

    parentReps = new Map<Id, Rep__c>([SELECT Id, Rep_Type__c, (SELECT Id, Type__c FROM Role_Histories__r WHERE End_Date__c = null ORDER BY CreatedDate DESC LIMIT 1) FROM Rep__c WHERE Id IN :listIds]);

    for(Rep__c reps :parentReps.values()) {

        for(Role_History__c roles :reps.Role_Histories__r) {
            reps.Rep_Type__c = roles.Type__c;
        }              
    }
    update parentReps.values();
}
if(Trigger.isUpdate) {
    for(Role_History__c roles :Trigger.New) {
        if(roles.End_Date__c != null) {
            roles.Type__c = null;
        }            
    }
  }
}

i though this would work but i'm getting this error-

RoleHistoryAfterTrigger: execution of AfterUpdate caused by: System.FinalException: Record is read-only Trigger.RoleHistoryAfterTrigger: line 29, column 1

the record is definitely not read-only because when i comment out the if(Trigger.isUpdate) code i no longer get the error. what am i missing to get this to function properly?

1

added

According to Order of Execution, after triggers are executed AFTER ves the record to the database, but doesn't commit yet. So, it is not logical to manipulate the record which has been sent to database already.

You need to make changes in the record BEFORE sending the record to database i.e., before update


If you are changing the trigger context records, you should always use before context for both update or insert. You can use Trigger.operationType and System.TriggerOperation.BEFORE_UPDATE:

trigger RoleHistoryAfterTrigger on Role_History__c (before update, after update, after insert, before insert) {

    Map<Id, Rep__c> parentReps = new Map<Id, Rep__c>();
    List<Id> listIds = new List<Id>();

    if(Trigger.operationType==System.TriggerOperation.BEFORE_INSERT) { // or AFTER_INSERT 
        // logic
    }
    if((Trigger.operationType==System.TriggerOperation.BEFORE_UPDATE)) {
        for(Role_History__c roles :Trigger.New) {
            if(roles.End_Date__c != null) {
                roles.Type__c = null;
            }            
        }
    }
}

Even better is when you use SWITCH statement:

switch on triggerEvent {

        when AFTER_INSERT, AFTER_UPDATE {
            //create related records
        }
        when BEFORE_INSERT {
            //set value on record create
        }
        when AFTER_DELETE {
            //prevent deletion of sensitive data
        }
        when else {
            //do nothing for AFTER_UNDELETE, BEFORE_DELETE, or BEFORE_UPDATE
        }
    }

You can refer to above link which has very good practices of trigger code

  • so to confirm, that despite me wanting this trigger to run after an existing record is updated, i still have to make it a before trigger? – testing Sep 23 at 20:10
  • added in answer – salesforce-sas Sep 24 at 2:47
  • not working properly, i’m going to ask a new question as i left out some details i think i should include – testing Sep 24 at 15:41
0

Welcome to Salesforce Stack Exchange.

Very simple answer: if you move the update of your Role_History__c record into a before insert/before update trigger then it will work.

Longer: The different trigger events: before insert/after insert / before update / after update... allow you to do different things. Trigger code for the before... events run before a new or changed record is committed to the database. Changes to the record (Role_History__c) itself are best placed here. Trigger code for the after... events runs after (the before... events trigger code and) the commit of the new or changed records to the database. You're not allowed to update the record itself now anymore. You can however happily update other records that are not part of the current list of records processed by the trigger (or more precisely that are not modified in the current transaction). The after insert/after update trigger is a good place for the updates to the (related) Rep__c records, but the update of the Role_History__c records should happen in a before insert/before update trigger.

You may want to check the documentation on order of execution: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_order_of_execution.htm Quite interesting to see what the platform is doing.

  • so to confirm, that despite me wanting this trigger to run after an existing role history record is updated, i still have to make it a before trigger? – testing Sep 23 at 20:14
  • Yes :-) Both trigger events happen after saving a role history record, before/after update in that case really means before the change is written to the database vs after the change has been written to the database. – Magnus Kreth Sep 23 at 20:28

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.