function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Vipin  PulinholyVipin Pulinholy 

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 . 

Best Answer chosen by Admin (Salesforce Developers) 
Paul.FoxPaul.Fox

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

ReidCReidC
Check out these docs on how to handle bulk.  If you coded to your pseudocode, you would hit governors fairly quickly.
Paul.FoxPaul.Fox

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; } }

 

 

 

This was selected as the best answer
Vipin  PulinholyVipin Pulinholy
Thank You Paul
David81David81

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.

michaellatideasmichaellatideas

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

michaellatideasmichaellatideas

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!

Paul.FoxPaul.Fox

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.

 

 

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>();
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(string.valueOf(t.WhoId).startsWith('00Q'))
	LeadIDs.add(t.WhoId);
	if(string.valueOf(t.WhoId).startsWith('003'))
	ContactIDs.add(t.WhoId);
	}
}

if(Trigger.isDelete || Trigger.isUpdate){
	for(Task t: Trigger.old){
	if(string.valueOf(t.WhoId).startsWith('00Q'))
	LeadIDs.add(t.WhoId);
	if(string.valueOf(t.WhoId).startsWith('003'))
	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;
}


}

 

@isTest
private class TestOpenTaskUpdate {
static testMethod void LeadTest(){
// Create a Lead
Lead newLead = new Lead(LastName='Test', Company='ABC', Status='Targeted');
insert newLead;

// Create a Task
Task newTask = new Task(Subject='Open Task', Status='Not Started', WhoId=newLead.Id);
test.startTest();
insert newTask;
test.stopTest();

// Verify that the # Open Tasks is correct
newLead = [select Open_Tasks__c from Lead where Id=:newLead.Id ];
System.assertEquals(1,newLead.Open_Tasks__c);
}

static testMethod void ContactTest(){
// Create a Contact
Contact newContact = new Contact(LastName='Test');
insert newContact;

// Create a Task
Task newTask = new Task(Subject='Open Task', Status='Not Started', WhoId=newContact.Id);
test.startTest();
insert newTask;
test.stopTest();

// Verify that the # Open Tasks is correct
newContact = [select Open_Tasks__c from Contact where Id=:newContact.Id];
System.assertEquals(1,newContact.Open_Tasks__c);
}

static testMethod void CompletedTest(){
// Create a Lead
Lead newLead = new Lead(LastName='Test', Company='ABC', Status='Targeted');
insert newLead;

// Create a Completed Task
Task newTask = new Task(Subject='Open Task', Status='Completed', WhoId=newLead.Id);
test.startTest();
insert newTask;
test.stopTest();

// Verify that the # Open Tasks is empty
newLead = [select Open_Tasks__c from Lead where Id=:newLead.Id ];
System.assertEquals(0,newLead.Open_Tasks__c);
}

static testMethod void BatchTest(){
// Create 100 Leads
List<Lead> newLeads = new List<Lead>();
for (Integer i = 0; i < 200; i++) {
newLeads.add(new Lead(LastName='Test '+i, Company='ABC', Status='Targeted'));
}
insert newLeads;

// Create a task for each one
List<Task> newTasks = new List<Task>();
for(Lead l : newLeads){
newTasks.add(new Task(Subject='Open Task', Status='Completed', WhoId=l.Id));
}

// Insert the tasks
test.startTest();
insert newTasks;
test.stopTest();

// We could verify that the Open Tasks fields were updated correctly,
// but this is just testing the governor limits so it's not necessary
}
}

 

 

Paul.FoxPaul.Fox

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.

michaellatideasmichaellatideas

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.

Paul.FoxPaul.Fox

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.

David81David81

 


Paul.Fox wrote:

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.


That's what I get for not counting brackets :)

 

stollmeyerastollmeyera

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.

Paul.FoxPaul.Fox

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.

stollmeyerastollmeyera

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 :)

Paul.FoxPaul.Fox

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

stollmeyerastollmeyera

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.

Paul.FoxPaul.Fox

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.

stollmeyerastollmeyera

If only it were that easy :)

gnatraegnatrae

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.

Paul.FoxPaul.Fox

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.

NatalieNatalie

Hi Paul,

Thanks for your fast follow up.  I have been working on it this week, but I think I'm figuring out that I may be a little too new to apex to figure out how to make the modifications myself.  It's exciting to know it can be done, just wish it was something a person could learn in a day :)

gnatrae
Eric FortenberryEric Fortenberry

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!

 

trigger CalculateOpenTasks 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>();
public set<Id> ContactIDs = new Set<Id>();
public list<Contact> ContactsToUpdate = new List<Contact>();
public set<Id> AccountIDs = new Set<Id>();
public list<Account> AccountsToUpdate = new List<Account>();

// Build the list of Leads, Contacts, and Accounts 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)
            LeadIDs.add(t.WhoId);
            if(string.valueOf(t.WhoId).startsWith('003')&&t.IsClosed==False)
            ContactIDs.add(t.WhoId);
            if(string.valueOf(t.WhoId).startsWith('001')&&t.IsClosed==False)
            AccountIDs.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);
            if(string.valueOf(t.WhoId).startsWith('001')&&t.IsClosed==False)
            AccountIDs.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;
}

// Update the Accounts

if(AccountIDs.size()>0){
for(Account a: [Select a.Id, a.Open_Tasks__c,
(Select Id From Tasks where IsClosed = False)
From Account a where Id in :AccountIDs])
AccountsToUpdate.add(new Account(Id=a.Id, Open_Tasks__c = a.Tasks.size()));
update AccountsToUpdate;
}

}

 

David81David81

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.

Paul.FoxPaul.Fox

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.

David81David81

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.

Eric FortenberryEric Fortenberry

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.

 

trigger CalculateOpenTasks 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>();
public set<Id> ContactIDs = new Set<Id>();
public list<Contact> ContactsToUpdate = new List<Contact>();
public set<Id> AccountIDs = new Set<Id>();
public list<Account> AccountsToUpdate = new List<Account>();

// Build the list of Leads, Contacts, and Accounts 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)
            LeadIDs.add(t.WhoId);
            if(string.valueOf(t.WhoId).startsWith('003')&&t.IsClosed==False)
            ContactIDs.add(t.WhoId);
        } else
        if(t.WhatId<>null){    
            if(string.valueOf(t.WhatId).startsWith('001')&&t.IsClosed==False)
            AccountIDs.add(t.WhatId);
        } else
        if(t.AccountId<>null){    
            if(string.valueOf(t.AccountId).startsWith('001')&&t.IsClosed==False)
            AccountIDs.add(t.AccountId);
        }
    }
}

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);
        } else
        if(t.WhatId<>null){
            if(string.valueOf(t.WhatId).startsWith('001')&&t.IsClosed==False)
            AccountIDs.add(t.WhatId);
        } else
        if(t.AccountId<>null){
            if(string.valueOf(t.AccountId).startsWith('001')&&t.IsClosed==False)
            AccountIDs.add(t.AccountId);           
        }
    }
}

// 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;
}

// Update the Accounts

if(AccountIDs.size()>0){
for(Account a: [Select a.Id, a.Open_Tasks__c,
(Select Id From Tasks where IsClosed = False)
From Account a where Id in :AccountIDs])
AccountsToUpdate.add(new Account(Id=a.Id, Open_Tasks__c = a.Tasks.size()));
update AccountsToUpdate;
}

}

 

 

 

Paul.FoxPaul.Fox

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.

Paul.FoxPaul.Fox

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.

Paul.FoxPaul.Fox

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.

Eric FortenberryEric Fortenberry

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!

 

trigger CalculateOpenTasks 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>();
public set<Id> ContactIDs = new Set<Id>();
public list<Contact> ContactsToUpdate = new List<Contact>();
public set<Id> AccountIDs = new Set<Id>();
public list<Account> AccountsToUpdate = new List<Account>();

// Build the list of Leads, Contacts, and Accounts 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)
            LeadIDs.add(t.WhoId);
            if(string.valueOf(t.WhoId).startsWith('003')&&t.IsClosed==False)
            ContactIDs.add(t.WhoId);
        } 
        if(t.WhatId<>null){    
            if(string.valueOf(t.WhatId).startsWith('001')&&t.IsClosed==False)
            AccountIDs.add(t.WhatId);
        }
        if(t.AccountId<>null){    
            if(t.IsClosed==False)
            AccountIDs.add(t.AccountId);
        }
    }
}

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);
        }
        if(t.WhatId<>null){
            if(string.valueOf(t.WhatId).startsWith('001')&&t.IsClosed==False)
            AccountIDs.add(t.WhatId);
        }
        if(t.AccountId<>null){
            if(t.IsClosed==False)
            AccountIDs.add(t.AccountId);           
        }
    }
}

// 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;
}

// Update the Accounts

if(AccountIDs.size()>0){
for(Account a: [Select a.Id, a.Open_Tasks__c,
(Select Id From Tasks where IsClosed = False)
From Account a where Id in :AccountIDs])
AccountsToUpdate.add(new Account(Id=a.Id, Open_Tasks__c = a.Tasks.size()));
update AccountsToUpdate;
}

}

 

Paul.FoxPaul.Fox

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.

Eric FortenberryEric Fortenberry

I tried getting all the WhatIds then adding them to the AccountIDs, but it's still not working...

 

trigger CalculateOpenTasks 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>();
public set<Id> ContactIDs = new Set<Id>();
public list<Contact> ContactsToUpdate = new List<Contact>();
public set<Id> AccountIDs = new Set<Id>();
public set<Id> WhatIDs = new Set<Id>();
public list<Account> AccountsToUpdate = new List<Account>();

// Build the list of Leads, Contacts, and Accounts 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)
            LeadIDs.add(t.WhoId);
            if(string.valueOf(t.WhoId).startsWith('003')&&t.IsClosed==False)
            ContactIDs.add(t.WhoId);
        } 
        if(t.WhatId<>null){    
            if(string.valueOf(t.WhatId).startsWith('001')&&t.IsClosed==False)
            WhatIDs.add(t.WhatId);
        }
        if(t.AccountId<>null){    
            if(t.IsClosed==False)
            AccountIDs.add(t.AccountId);
        }
    }
}

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);
        }
        if(t.WhatId<>null){
            if(string.valueOf(t.WhatId).startsWith('001')&&t.IsClosed==False)
            WhatIDs.add(t.WhatId);
        }
        if(t.AccountId<>null){
            if(t.IsClosed==False)
            AccountIDs.add(t.AccountId);           
        }
    }
}

// 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;
}

// Update the Accounts
if (WhatIDs.size()>0) {
for(Account a: [Select a.Id,
(Select Id From Tasks where IsClosed = False)
From Account a where Id in :WhatIDs])
AccountIDs.add(a.Id);
}

if(AccountIDs.size()>0){
for(Account a: [Select a.Id, a.Open_Tasks__c,
(Select Id From Tasks where IsClosed = False)
From Account a where Id in :AccountIDs])
AccountsToUpdate.add(new Account(Id=a.Id, Open_Tasks__c = a.Tasks.size()));
update AccountsToUpdate;
}

}

 

Vishal (Sops)Vishal (Sops)

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

Vishal (Sops)Vishal (Sops)

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

David81David81

Vishal (Sops) wrote:

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..


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.

Vishal (Sops)Vishal (Sops)

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.

 

 

David81David81

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:

system.debug('***OWNER ROLE ID = ' + t.Owner.UserRoleID);

 That way you can verify what RoleID is making into that block of code.

Vishal (Sops)Vishal (Sops)

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
trigger CalculateOpenTasks 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>();
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..

David81David81

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.

 

Vishal (Sops)Vishal (Sops)

Yes I did, and this is what log says:-

 

23:28:33:127 USER_DEBUG [17]|DEBUG|t.owner.UserRoleID = null

Jerun JoseJerun Jose
Have you queried this field in your code ? If 't' is just a variable pointing to your trigger content, then you cannot use it to get parent record info.
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
Vishal (Sops)Vishal (Sops)

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.

Jerun JoseJerun Jose

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.

 

trigger CalculateOpenTasks on Task (after delete, after insert, after undelete, 
after update) {
list<Task> TaskList = null;
if(!trigger.isDelete)
	TaskList = [select id, otherFields, Owner.UserRoleID from Task where id in :trigger.new];

 And change this seciton

if(Trigger.isInsert || Trigger.isUnDelete || Trigger.isUpdate){
    for(Task t: Trigger.new){
 

 to

if(Trigger.isInsert || Trigger.isUnDelete || Trigger.isUpdate){
    for(Task t: TaskList){
 

 Make sure you change the query for TaskList to query all the fields that you are using in the trigger.

 

David81David81

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.

Vishal (Sops)Vishal (Sops)

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?

Jerun JoseJerun Jose
You cannot deactivate workflows and validation rules though apex (not easily atleast).

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.
Vishal (Sops)Vishal (Sops)

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..

 

 

Jerun JoseJerun Jose
When the trigger tries to save the contact record, that is when the validation rules will kick in and check the data. Now if your trigger is saving the contacts in such a way that they pass the validations, then the validation rules will stop being a hinderance for the contact save.

Be wary of using this type of a solution, as any new validation rules that are built will also need to consider this situation
ForceLoverForceLover

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.