0

I am trying to count all open activities related to an account. I have a controller class and two triggers on task and events. I am getting the right records for a related account but, I can not update the field because it gives,

System.FinalException: Record is read-only

The field I have on account is called open activities with api of open_activities__c when my code reaches to this section, It throws the above exception Below is the code I am trying to implement.

public class ActivityCounter {

   public static void activityCount(List<Account> lst) { 
   List<Account> upd = new List<Account>();
   Set<ID> accidset = new Set<ID>();
   Integer TaskCount = 0;
   Integer EventCount = 0;
   for(Account acc: lst) {
   accidset.add(acc.Id);
   } 
   List<Task> tskLst = [Select Id From Task Where WhatId IN:accidset and Status!='Completed']; 
   TaskCount = tskLst.size();    
   System.debug('Size of open tasks: '+ TaskCount);    
   List<Event> evntLst = [Select Id From Event Where WhatId IN:accidset and  EndDateTime <= TODAY];
   EventCount = evntLst.size();
   System.debug('Size of open events: '+ EventCount);
   for(Account ac:lst) {
   ac.Open_Activities__c=TaskCount+EventCount;
   upd.add(ac);    
   }    
   update upd;

  }
}

and the triggers

trigger ActivityCountTrigger on Task (after insert,after update) {
 ActivityCounter.activityCount(Trigger.New);
}
trigger ActivityCountTrigger on Events (after insert,after update) {
 ActivityCounter.activityCount(Trigger.New);
}

The open_activities__c field is not read-only. I double checked it. I have no clue why I am getting this exception. Please help me understanding the issue here. Thank you.

3
  • 1
    Have you considered using the Declarative Lookup Rollup Summary tool? appexchange.salesforce.com/…
    – Nick Cook
    Aug 23 '19 at 0:18
  • That will probably be the best answer for the OP Aug 23 '19 at 0:28
  • @NickCook, No, I have not. Since I am practicing apex, So, I wanted to do this using apex only. Aug 27 '19 at 6:42
1

So this code is not going to work for a number of reasons:

  1. Your trigger is on Task, and you send Trigger.New to the ActivityCounter class. However, that class expects List<Account>, which is never going to be sent from those two triggers.

  2. While I applaud your intent to bulkify, you're getting all the open tasks/events for all accounts. Not for each account. What you really want is an aggregate query that groups your tasks/events by AccountId and THEN you update your accounts.

Here is what I would do:

  1. Get your counter properly set (I changed the header in preparation for my next suggestion)
public static void activityCount(Set<Id> accountIds) { 
   List<Account> acctToUpdate = new List<Account>();

   Map<Id, Double> tasksByAccount = new Map<Id, Double>();
   for (AggregateResult agg : [Select WhatId, count(Id) cnt From Task Where WhatId IN :accountIds and Status != 'Completed' GROUP BY WhatId]) {
     Id accId = (Id) agg.get('WhatId');
     Double cnt = (Double) agg.get('cnt');

     tasksByAccount.put(accId, cnt);
   }

   Map<Id, Double> eventsByAccount = new Map<Id, Double>();
   for(AggregateResult> agg : [Select  WhatId, count(Id) cnt From Event Where WhatId IN :accountIds and  EndDateTime <= TODAY GROUP BY WhatId]) {
     Id accId = (Id) agg.get('WhatId');
     Double cnt = (Double) agg.get('cnt');

     eventsByAccount.put(accId, cnt);
   }

   for(Id accountId : accountIds) {
     Integer taskCount = tasksByAccount.containsKey(accountId) ? tasksByAccount.get(accountId) : 0;
     Integer eventCount = eventsByAccount.containsKey(accountId) ? eventsByAccount.get(accountId) : 0;

     Account acc = new Account(Id = accountId, Open_Activities__c=(taskCount + eventCount) );

     acctToUpdate.add(ac);    
   }    

   update acctToUpdate;

  }
  1. Get your triggers to pass a Set<Id> instead of a List<Account>. One way to do this (for Tasks, I leave events to you) would be like this
trigger ActivityCountTrigger on Task (after insert, after update) {
  Set<Id> acctIds = new Set<Id>();

  for (Task t : trigger.New) {
    //I Have to check this is an account and not something else
    if (t.WhoId.startsWith(Account.sObjectType.getDescribe().getKeyPrefix()) {
      acctIds.add(t.WhoId);
    }
  }

  ActivityCounter.activityCount(acctIds);
}

Notes:

  • There are many more improvements to make here (namely trying to consolidate both queries, using IsClosed and verifying that the status closed in your trigger)
  • I posted this without being able to compile it (was not in a place where I could do so), please excuse and work-around any typos.
2
  • Yes, you want whatId and not whoId. That's what happens when you type directly here and can't test your code. I also fixed a couple of minor things, but this should still work. Aug 23 '19 at 14:56
  • Thank you Sebastian. I had made changes in the previous code you suggested same like you did here. It worked for me and now I know a way on how to do such kind of task. Aug 26 '19 at 12:53
0

The problem is you're passing Trigger.new into your activityCount() method.

The activityCount() method is expecting a list of Account records, but Trigger.new is either a list of Task or Event records.

You're then adding the items within the trigger list to your upd variable, and because this is an after trigger, you cannot perform DML on it.

8
  • He should be able to perform DML on accounts if the trigger is Task/Event, as it is indicated in the post Aug 23 '19 at 0:24
  • But it's the passed in trigger that he's trying to perform DML on, not a freshly created list of accounts, hence the 'Record is read-only' exception. The question was about the exception after all.
    – Nick Cook
    Aug 23 '19 at 0:28
  • 1
    lol. I assumed that because he's got the exception, it must have compiled somehow.
    – Nick Cook
    Aug 23 '19 at 0:30
  • 1
    Yeah, I think he started on an Account trigger, got the error, and later realized there were other things wrong. Aug 23 '19 at 0:30
  • 1
    @SebastianKessel, You are right. I started on account trigger and it was compiling and later i realized that i need the open activities field updated when a task or event is created. So, I changed the trigger from account to task and event. But, I did not compile it before posting the question here. I was getting the exception while i was compiling the account trigger. I did not realize before posting my question about that stupid mistake I am making by changing account trigger to task and event trigger. Thank you for pointing out. I will give a try to what you have suggested. Aug 23 '19 at 0:50

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.