[SalesForce] System.FinalException: Record is read-only. How to update fields in event object

i wrote a function inside event trigger handler that should receive custom times for a meeting, a custom time zone of the customer, and a timezone for the user – and calculate the time of the meeting according to the user's timezone.

for example:

Customer_Time_Zone__c : GMT -5

User's Time Zone: GMT + 2

Start_Customer_Time_Zone__c : 10:00am

End_Customer_Time_Zone__c: 11:00am

The function will receive this data and calculate that the standard time of the meeting is 17:00pm-18:00 in the user's timezone, and fill the event standard time fields StartDateTime, EndDateTime.

The problem is: i can't do that, because the fields are read only. i have no idea how to update these fields, and if there are more fields that are related to them such as duration etc.

Here is the function so far:

private static void updateStandartDateTimeFields(User u, Event e)
    {
        System.debug('updateStandartDateTimeFields start');
        System.debug('User: ' + u);
        System.debug('Event: ' + e);

        e.StartDateTime = e.Start_Customer_Time_Zone__c;
        e.EndDateTime = e.End_Customer_Time_Zone__c;

        Update e;
    }

When I run a test I receive the following error:

System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, EventTrigger: execution of AfterInsert
caused by: System.FinalException: Record is read-only

Class.EventTriggerHandler.updateStandartDateTimeFields: line 131, column 1
Class.EventTriggerHandler.createMeeting: line 59, column 1
Class.EventTriggerHandler.afterInsert: line 31, column 1
Class.TriggerHandler.run: line 45, column 1
Trigger.EventTrigger: line 13, column 1: []

Can I avoid this error? If so, how?

Best Answer

Field updates belong in a before trigger and do not require additional DML operations. I suggest you have a read of Triggers from the Apex Developer Guide:

You can define triggers for top-level standard objects that support triggers, such as a Contact or an Account, some standard child objects, such as a CaseComment, and custom objects. To define a trigger, from the object management settings for the object whose triggers you want to access, go to Triggers.

There are two types of triggers:

  • Before triggers are used to update or validate record values before they’re saved to the database.
  • After triggers are used to access field values that are set by the system (such as a record's Id or LastModifiedDate field), and to affect changes in other records, such as logging into an audit table or firing asynchronous events with a queue. The records that fire the after trigger are read-only.

If you really must use an after context, you can't operate on the context records, but rather must create new ones.

List<Event> toUpdate = new List<Event>();
for (Event record : trigger.new)
{
    toUpdate.add(new Event(Id=record.Id, /*other data*/));
}
update toUpdate;
Related Topic