You need to sign in to do that
Don't have an account?
Show Open Task Count on Lead Detail Page- Bulk Trigger
Hi All,
I have a requirement to show # of open Task on Lead Detailpage. Below is the logic that we are planning to implement. I would like tocheck with you if any one has implemented similar functionality handling BULK insert/update.If so could you please share the code?
Trigger on Task <after insert after update>
{
for (Task thistask : Trigger.new)
{
// Get all leads assosited to Tasks
if (WhoId.startsWith('00Q'))
leadIdToUpdateList.add(taskWhoId.WhoId);
}
FOR each Lead
SelectCount() from Task where whoId=LeadId and Status=Completed
Updatecustom field “ Open Task” on Lead object
End Loop
}
Thank You .
I've been meaning to write this for a while. Here's my rough draft. You should write some test cases including bulk test cases before deploying this to production.
trigger UpdateLeadOpenTasks on Task (after delete, after insert, after undelete, after update) { // Declare the variables public set<Id> LeadIDs = new Set<Id>(); public list<Lead> LeadsToUpdate = new List<Lead>(); // Build the list of Leads to update for(Task t: Trigger.new){ if(string.valueOf(t.WhoId).startsWith('00Q')) LeadIDs.add(t.WhoId); System.debug('WhoId = ' + t.WhoId); } // Update the Leads if(LeadIDs.size()>0){ for(Lead l: [Select l.Id, l.Open_Tasks__c, (Select Id From Tasks where IsClosed = False) From Lead l where Id in :LeadIDs]) LeadsToUpdate.add(new Lead(Id=l.Id, Open_Tasks__c = l.Tasks.size())); update LeadsToUpdate; } }
All Answers
I've been meaning to write this for a while. Here's my rough draft. You should write some test cases including bulk test cases before deploying this to production.
trigger UpdateLeadOpenTasks on Task (after delete, after insert, after undelete, after update) { // Declare the variables public set<Id> LeadIDs = new Set<Id>(); public list<Lead> LeadsToUpdate = new List<Lead>(); // Build the list of Leads to update for(Task t: Trigger.new){ if(string.valueOf(t.WhoId).startsWith('00Q')) LeadIDs.add(t.WhoId); System.debug('WhoId = ' + t.WhoId); } // Update the Leads if(LeadIDs.size()>0){ for(Lead l: [Select l.Id, l.Open_Tasks__c, (Select Id From Tasks where IsClosed = False) From Lead l where Id in :LeadIDs]) LeadsToUpdate.add(new Lead(Id=l.Id, Open_Tasks__c = l.Tasks.size())); update LeadsToUpdate; } }
Just wanted to pop in and say thanks to Paul.
I had a similar request to flag Leads that had open Tasks. I had it working great all by myself until our Sales group threw another wrench in the works and asked that the flag only be set if the current owner of the Lead has an open Task for said Lead.
Since my code didn't play nice with this new request I started looking for other ideas and stumbled on this thread.
After a bit of modification I've got it working.
One change I would suggest, in general, is to move the update out of the second for loop to ensure you only do one update call.
Thanks again for the inspiration to make this work for our group.
This is fantastic. I'm converting this over for a similar use on Cases.
The only trick is that it looks like I'll have to do a second, similar trigger for Delete, because for that I need to use Trigger.old instead of Trigger.New
Actually, now that I've posted there is one other issue that I see -- if the task is reassigned in an update from one object to another, so you have to do both Trigger.old and Trigger.new to get both sides.
But this is very exciting once I get debugging!
Here's the final code, updated for deleting/updating/etc. No need to create a second trigger, just use the Trigger Context Variables. Works for Leads and Contacts, make sure to create the Open Activities field on both before creating the trigger. There's also a test method below, although you should probably add test methods for the ones you found: Deleting task, changing the task object.
Oh, and the update is outside of the for loop. There are no brackets around the for loop, so it should only do the next line of code. You could put in brackets if you're worried.
Thanks! That's pretty similar to what I worked out for Cases; and I didn't use the second trigger either as I was working wth the code more.
For anyone else that looks at this, I've put this into a unmanaged package to distribute to the orgs I work in. Download Here.
Note: If you have any validation rules or required fields on the task, you will have to check the box to 'Ignore Apex Errors', and then update the test cases after installing.
That's what I get for not counting brackets :)
Sorry in advance for reactivating such an old topic. This is some great info, but I am curious if this can be turned into a before trigger instead of an after. I am trying to bypass validation rules. Any help would be greatly appreciated.
I'm not sure how a before trigger will bypass your validation rules. It will still need to be validated after the trigger runs. Due to the cross-object updates and queries, this trigger should be an after trigger.
That being said, a before trigger is possible but it will take longer to save the task because it has to do the soql query and update before the task can be saved. Also, you'll need to modify the count slightly for certain scenarios. For example, if it is a before trigger and it fires when a task is created, then it will not be counted in the SOQL query since it is not created yet.
I was under the impression that an after trigger gets hit by validation rules whereas a before trigger does not. However, looking back on it I think I may have misunderstood. At this point I think the only workaround is to create a custom checkbox field, check it off before the update and then uncheck it after the update. You can then build this into your validation rules to only run if the checkbox is false. Really cheesy workaround, but what can you do :)
The trigger that we're discussing is only designed to update the Lead or Contact anyway, I'm not quite sure why validation rules matter at all. You want to give us an idea of what you're validating and why it affects this thread?
But before and after triggers both get validated. The only thing that doesn't get validated is workflow field updates that are run a second time if they don't cause before and after triggers to run again and cause any updates.
Triggers and Order of Execution
The reason I want to get passed the validation rules is because this trigger makes an update to the lead. This lead update causes a miniscule validation rule to fire which stops the lead update from ever happening. I have searched around for this and it has come up many other times. The common work-around is to create a checkbox field and incorporate that into the validation rules and triggers, like I mentioned in my previous post.
I don't know how big of an issue this is, but my first thought is that you should update the Leads so that you have a database full of valid leads, and then you won't have to worry about workarounds.
If only it were that easy :)
This information is really great. However, I'm pretty new to apex coding and I'm really needing to use some code like this for a custom object. Basically, we bill our consulting days by using events, and I need to write a trigger that determines whether or not the event is completed. Could I use/edit this code for that? It seems like I should be able to.
Yes, you can use this code. Just need to change the Activity query to look for events (IsTask = False). Keep in mind that events are automatically marked as completed as soon as the date has passed, so you may want to setup a custom field where users can actually say that it was accomplished. Also, if you have multiple invitees, you'll have to filter out the copies of each event from the query, I forget which field that is, but I think it's something like IsInvitation.
Hi Paul,
gnatrae
Paul,
I tried to update the CalculateOpenTasks trigger to make this work for accounts, but it's not updating the Open Task field on the account. Any ideas what I'm missing? Thanks!
Eric,
An Account ID will never be in the WhoID field of a Task. You'll want to be checking the WhatID field for Accounts. Only Leads and Contacts are used as WhoIDs.
Eric,
David is right, you won't find the Account in the WhoID. However, you'll want to check the AccountId field AND the WhatId field. If the Task is related to a Contact, the AccountId field will be filled in automatically, and the WhatId might be blank.
Good point Paul. I always seem to forget about that one since it's one of those weird read-only fields that you can only really get to in code.
I tried to update this code per David and Paul's suggestions, but still can't seem to get it to work. I suspect my if else statements to check WhatId and AccountId have something wrong.
Take out all the else statements, you just want three if statements (no else)
It is possible to have a WhoId, WhatId, and AccountId, and you would want to update the Contact, the Account, and possible a different Account, so you want all three if statements to be processed, they are not mutually exclusive.
Also, this isn't going to mess anything up, but you don't have to check if AccountId starts with 001, if it's not null then it's going to be an Account Id.
Also, you may want to check if that query you have in the Update Accounts section actually returns all Tasks where the AccountId is filled in but the WhatId might be blank. You might have to use a more complicated update function for accounts since they can be related to Tasks through the WhatId or the AccountId field.
You are correct about that the query in the updating accounts section is not returning all of the tasks. It is only counting ones that are directly related to the AccountId (and not through WhatId). Can you help me update the query so that it pulls all the tasks? Thanks!
You'll need to do an aggregate query on Tasks where WhatId in :AccountIds, grouping by the WhatId and counting the Ids. Then you'll have to add that value to the result from the other query.
I tried getting all the WhatIds then adding them to the AccountIDs, but it's still not working...
Thanks a lot for your code. My requirement is a bit complex.
I just want to count those open activities which have either 30 days prior due date or 90 days futre due date. Also If I could do it based on task owner's role, that would be great.
Best Regards,
Vishal Sharma
Is there any way to condition user role id or profile ID in statement below
if(string.valueOf(t.WhoId).startsWith('00Q')&&t.IsClosed==False &&t.ownerRoleID!='xyz')
I tried to do this but it says there is no keyword like ownerroleID, what to do? I want this logic to run on specific role ID's only..
Best Regards,
Vishal sharma
I believe you are looking for "t.Owner.UserRoleID" although you may wish to use "t.Owner.UserRole.Name" to avoid hardcoding IDs in case of mismatches between orgs.
David, thanks for your reply. t.owner.UserRoleID is not working. Though it doesn't show any error while saving the trigger but it's not doing what I want it to.
I want that the trigger should run only on specific Roles/Profiles. User Role/Profile here belongs to the user whom the task has been assigned to (task owner). No matter who assigned the task, could be a different user from another role. How to extract this role from task owner, that is the problem. here is my code
if(string.valueOf(t.WhoId).startsWith('00Q')&&t.IsClosed==False && t.Owner.UserRoleID !='00E40000000oN86')
LeadIDs.add(t.WhoId);
I just don't want this code to run if task owner's user role id is 00E40000000oN86, but the code above is not working. Please see if you can give some inputs.
Are you sure that it is not working as expected? Try throwing a debug statement inside that if statement to verify that your code is being skipped as expected. Perhaps something like:
That way you can verify what RoleID is making into that block of code.
David, I am very new to write code, did some object oriented coding in college time though :) have started digging into developer force just a couple of days before. I got this code to count no of open activities under a contact/lead in this board only. here is whole code..
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
after update) {
// Declare the variables
public set<Id> LeadIDs = new Set<Id>();
public list<Lead> LeadsToUpdate = new List<Lead>();
public set<Id> ContactIDs = new Set<Id>();
public list<Contact> ContactsToUpdate = new List<Contact>();
// Build the list of Leads and Contacts to update
if(Trigger.isInsert || Trigger.isUnDelete || Trigger.isUpdate){
for(Task t: Trigger.new){
if(t.WhoId<>null){
if(string.valueOf(t.WhoId).startsWith('00Q')&&t.IsClosed==False) //here I need to put condition to exclude some user role but don't know how?
LeadIDs.add(t.WhoId);
if(string.valueOf(t.WhoId).startsWith('003')&&t.IsClosed==False)
ContactIDs.add(t.WhoId);
}
}
}
if(Trigger.isDelete || Trigger.isUpdate){
for(Task t: Trigger.old){
if(t.WhoId<>null){
if(string.valueOf(t.WhoId).startsWith('00Q')&&t.IsClosed==False)
LeadIDs.add(t.WhoId);
if(string.valueOf(t.WhoId).startsWith('003')&&t.IsClosed==False)
ContactIDs.add(t.WhoId);
}
}
}
// Update the Leads
if(LeadIDs.size()>0){
for(Lead l: [Select l.Id, l.Open_Tasks__c,
(Select Id From Tasks where IsClosed = False)
From Lead l where Id in :LeadIDs])
LeadsToUpdate.add(new Lead(Id=l.Id, Open_Tasks__c = l.Tasks.size()));
update LeadsToUpdate;
}
// Update the Contacts
if(ContactIDs.size()>0){
for(Contact c: [Select c.Id, c.Open_Tasks__c,
(Select Id From Tasks where IsClosed = False)
From Contact c where Id in :ContactIDs])
ContactsToUpdate.add(new Contact(Id=c.Id, Open_Tasks__c = c.Tasks.size()));
update ContactsToUpdate;
}
}
the one and only issue is that I need this code to run only for some specific user roles or in other words, want to exclude some user roles. Can you direct me what changes or extra lines of code need to be there..
Did you try using the debug statement to see what UserRoleID was getting into your code? You need to verify what is actually happening to troubleshoot it. Add the debug statement and then fire up the developer console before making your edits/inserts to test. Check the debug logs and verify the ID is what you expect.
Yes I did, and this is what log says:-
23:28:33:127 USER_DEBUG [17]|DEBUG|t.owner.UserRoleID = null
Although the code will compile and run, parent references through trigger content is always set to null. You will have to query for this info in your trigger and then use it in your code
Hi Jerun - Yes t is just representing task that's pointing to trigger content. So I got it that this could be issue here. Can you help me out in writing a query to extract Role ID from this t variable (task). Basically task's owner's role ID. Thanks a lot, really appreciate your help.
A simple fix would be to have a query that just collects data for the entire trigger.new content and stores them in a list. You can then use that list instead of trigger.new. I noticed that this trigger is all on after events and does not throw any errors, so it should be fine.
Have this piece at the top.
And change this seciton
to
Make sure you change the query for TaskList to query all the fields that you are using in the trigger.
My apologies. I should have caught that. Another option (depending on org size, of course) would be to query your user table up front and create a map of userIds to user objects. You could then just access this map while looping through your tasks.
Thanks a lot guys. I finally deployed my code in production after testing it in sandbox. Now I am facing another problem. My trigger work on tasks and update a custom field at contact. But all the validation rules on contacts are conflicting with the trigger. For example, if I am mass uploading tasks in SF, then the trigger tried to update the field at contact and then save the contact. But all the validation rules (such as no email id or Wrong mailing country) aren't letting it to save the contact, hence the errors. Is there any way I can deactivate all the workflows in my apex triggers just before it update the contacts and once update is done, it should enable the validation rules..
if(ContactIDs.size()>0){
for(Contact c: [Select c.Id, c.Open_Tasks__c,(Select Id, Ownerid From Tasks where IsClosed = False and ActivityDate >= :d AND ActivityDate < :e AND Owner.userrole.name not in: exclud)From Contact c where Id in :ContactIDs])
ContactsToUpdate.add(new Contact(Id=c.Id, Open_Tasks__c = c.Tasks.size()));
update ContactsToUpdate;
just before the last line, I think I need to deactivate all workflows, any take on this?
A few things that can be done in situations like this:
1. Have a custom field on contact object which is populated only by this apex trigger. Modify the workflows and validation rules to not fire if the value in this field is changed. You can then use this trigger to update that custom field with the current timestamp everytime it executes. This way your validation rules and workflows will ignore the contacts updated through apex.
2. Recreate the validation rules in the child object also, so that the child records themselves complain if the contact data is bad. It may be the best solution if only few validations are involved.
3. The more code heavy solution would be to use Database.UpdateResult to capture the save results for the update of contacts and then read through this to report errors on the child records that had the parent contact save failed. This would be the best way if you have too many validations and workflows in the contact object, or if maintaining the validations would be a concern.
I would like to do the first point. I haven't tried it yet but one question.
How can my trigger populate this new custom field at contact level if it's not getting saved because of validation rules in the first place?
Does ISCHANGED attribute holds true or false even before the record is saved? Thanks for bearing my stupid question but that's how we learn..
Be wary of using this type of a solution, as any new validation rules that are built will also need to consider this situation
Hi Everyone
I have a similar requirement like
We want to capture the Total Open Activities Events & Task) on the Lead & Opportunity object, When there are no Open Activities the Total Open Activities field should be 0..how i can achieve this please reply back me ASAP.