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
José Teixeira GomesJosé Teixeira Gomes 

Creating Follow Up Tasks

Hello, I am fairly new to Salesforce and even more to APEX so I am having some difficulties in writing a Trigger.

So I have managed to create a list of tasks that have been completed. Now I want to create new tasks in x number of days but I don't know how to insert a list of new tasks. All tasks would have the same structure ('Follow Up' + Account/Contact Name; assigned to the same person has before OwnerId; Not Started, WhatId the same as before). There is just a few things I need the code to do.

ActivityDate should be: Today + Contact_Frequency__c (Contact Custom Field, number field)
WhoId should be: Primary_Contact__c (Account Custom Field, lookup to contact)
Priority should be dependent on a Tier__c (Account Custom Field, formula field): High if it is Tier 1, else normal

I would have done this myself but I still don't know how to reference other objects fields

Then I have another question regarding shared activities (how to create separate tasks for the participants of a given event), but that is for another day.

All help is appreciated
Best Answer chosen by José Teixeira Gomes
pconpcon
You'll want to do loop through the WhoIds and the WhatIds and get all of your contacts.  Then build up all their information and create new tasks based on that information

Trigger followUpTasks on Task(after update) {
     Set<Id> completedTasks = new Set<Id>();
     Set<Id> contactIds = new Set<Id>();
     Set<Id> accountIds = new Set<Id>();

     for (Task task: Trigger.new) {
          Task oldTask = Trigger.oldMap.get(task.Id);

          if (task.Status == 'Completed' && task.Status != oldTask.Status) {
               completedTasks.add(task.Id);
               contactIds.add(task.WhoId);
               accountIds.add(task.WhatId);
          }
     }

     contactIds.remove(null);
     accountIds.remove(null);

     Map<Id, Contact> contactMap = new Map<Id, Contact>();
     Map<Id, Account> accountMap = new Map<Id, Account>();

     if (!contactIds.isEmpty()) {
          contactMap = new Map<Id, Contact>([
               select Contact_Frequency__c,
                    Name
               from Contact
               where Id in :contactIds
          ]);
     }

     if (!accountIds.isEmpty()) {
          accountMap = new Map<Id, Account>([
               select Primary_Contact__c,
                    Name,
                    Tier__c
               from Account
               where Id in :accountIds
          ]);
     }

     List<Task> newTasks = new List<Task>();
     for (Id id: completedTasks) {
          Task task = Trigger.newMap.get(id);
          Contact contact = contactMap.get(task.WhoId);
          Account account = accountMap.get(task.WhatId);

          if (account == null && contact == null) {
               continue;
          }

          Task newTask = new Task(
               Subject = 'Follow Up ' + account.Name + '/' + contact.Name,
               OwnerId = task.OwnerId,
               WhoId = account.Primary_Contact__c,
               Priority = (account.Tier__c == 1) ? 'High' : 'Normal'
          );

          Date dueDate = Date.today();
          dueDate.addDays(contact.Contact_Frequency__c);
          newTask.ActivityDate = dueDate;

          newTasks.add(newTask);
     }

     if (!newTasks.isEmpty()) {
          insert newTasks;
     }
}

NOTE: This code is untested and may contain typographical and logic errors

All Answers

pconpcon
You'll want to do loop through the WhoIds and the WhatIds and get all of your contacts.  Then build up all their information and create new tasks based on that information

Trigger followUpTasks on Task(after update) {
     Set<Id> completedTasks = new Set<Id>();
     Set<Id> contactIds = new Set<Id>();
     Set<Id> accountIds = new Set<Id>();

     for (Task task: Trigger.new) {
          Task oldTask = Trigger.oldMap.get(task.Id);

          if (task.Status == 'Completed' && task.Status != oldTask.Status) {
               completedTasks.add(task.Id);
               contactIds.add(task.WhoId);
               accountIds.add(task.WhatId);
          }
     }

     contactIds.remove(null);
     accountIds.remove(null);

     Map<Id, Contact> contactMap = new Map<Id, Contact>();
     Map<Id, Account> accountMap = new Map<Id, Account>();

     if (!contactIds.isEmpty()) {
          contactMap = new Map<Id, Contact>([
               select Contact_Frequency__c,
                    Name
               from Contact
               where Id in :contactIds
          ]);
     }

     if (!accountIds.isEmpty()) {
          accountMap = new Map<Id, Account>([
               select Primary_Contact__c,
                    Name,
                    Tier__c
               from Account
               where Id in :accountIds
          ]);
     }

     List<Task> newTasks = new List<Task>();
     for (Id id: completedTasks) {
          Task task = Trigger.newMap.get(id);
          Contact contact = contactMap.get(task.WhoId);
          Account account = accountMap.get(task.WhatId);

          if (account == null && contact == null) {
               continue;
          }

          Task newTask = new Task(
               Subject = 'Follow Up ' + account.Name + '/' + contact.Name,
               OwnerId = task.OwnerId,
               WhoId = account.Primary_Contact__c,
               Priority = (account.Tier__c == 1) ? 'High' : 'Normal'
          );

          Date dueDate = Date.today();
          dueDate.addDays(contact.Contact_Frequency__c);
          newTask.ActivityDate = dueDate;

          newTasks.add(newTask);
     }

     if (!newTasks.isEmpty()) {
          insert newTasks;
     }
}

NOTE: This code is untested and may contain typographical and logic errors
This was selected as the best answer
José Teixeira GomesJosé Teixeira Gomes
Hello pcon, first of all thank you very much for your help, people like you make the world a better place. But I am still having some issues:

One minor problem is that I can't add Contact_Frequency__C to the due date. I get this error:
Method does not exist or incorrect signature: [Date].addDays(Decimal)
I know that I need to add an Integer instead but is there any field that would be recognized as such. Contact_Frequency__C has 3 numbers and no decimals,

The other problem is that the code is not actually inserting a new task when I close one. I am not sure why this happens because the code makes a lot of sense to me and it seem to do everything I ask. Could it be because there is another trigger firing at the same time that changes the due date of completed tasks to today. This should not affect it, because they all fire at the same time right?

Thanks again for your help
pconpcon
The first error is easy enough to fix.  That should just be

dueDate.addDays((Integer) (contact.Contact_Frequency__c));

As for the trigger question, no they do not run at the same time, they run sequentially.  You are guaranteed that all triggers will run, but you are not guaranteed the order in which they will run.  They may run in the correct order for a while then change order.  If you have triggers that are dependent on ordering (or may be affected by ordering) most people move the individual trigger logic into a single apex class [1].  This will allow you to handle the ordering of the trigger fire as well as some other nice things like being able to cache/reduce your DML operations.

[1] http://blog.deadlypenguin.com/blog/2012/02/13/classifying-triggers-in-salesforce/
José Teixeira GomesJosé Teixeira Gomes
Hey pcon,

Again thank you so much for your help. I feel it is really close now. I was able to run the trigger once, but when I tested it a second I got this error:

FollowUpTask: execution of AfterUpdate caused by: System.NullPointerException: Attempt to de-reference a null object: Trigger.FollowUpTask: line 51, column 1

Tried to do something about it but had no success, given my lack of talent for this
José Teixeira GomesJosé Teixeira Gomes
Another thing, so apparentely the trigger works when I close a task that I create mannually. When I try to close the task created by the trigger I get the error message above.

The other trigger I have should not be affecting the new one. The only thing it does is to change the due date of completed tasks to today
pconpcon
So my guess is that it's because either the contact or the account id is null on your task.  (or it's a task that is associtated to an opportunity).  I thought I had covered this, but I goofed and line 47 should be

if (account == null || contact == null) {

This will mean that if your task is not associated with both an account AND a contact it will not create new ones
José Teixeira GomesJosé Teixeira Gomes
Trigger FollowUpTask on Task(after update) {

     Set<Id> completedTasks = new Set<Id>();
     Set<Id> contactIds = new Set<Id>();
     Set<Id> accountIds = new Set<Id>();
 
     for (Task task: Trigger.new) {
     
          if (task.Status != Trigger.oldMap.get (task.Id).Status && task.Status=='Completed')
               completedTasks.add(task.Id);
               contactIds.add(task.WhoId);
               accountIds.add(task.WhatId);
          }
 
     contactIds.remove(null);
     accountIds.remove(null);
 
     Map<Id, Contact> contactMap = new Map<Id, Contact>();
     Map<Id, Account> accountMap = new Map<Id, Account>();
 
     if (!contactIds.isEmpty()) {
          contactMap = new Map<Id, Contact>([
               select Contact_Frequency__c,
                    Name
               from Contact
               where Id in :contactIds
          ]);
     }
 
     if (!accountIds.isEmpty()) {
          accountMap = new Map<Id, Account>([
               select Primary_Contact__c,
                    Name,
                    Tier__c
               from Account
               where Id in :accountIds
          ]);
     }

     List<Task> newTasks = new List<Task>();
     for (Id id: completedTasks) {
          Task task = Trigger.newMap.get(id);
          Contact contact = contactMap.get(task.WhoId);
          Account account = accountMap.get(task.WhatId);
 
          if (account == null && contact == null) {
               continue;
          }
 
          Task newTask = new Task(
               Subject = 'Follow Up ' + account.Name + '/' + contact.Name,
               OwnerId = task.OwnerId,
               WhoId = account.Primary_Contact__c,
               Priority = (account.Tier__c == 'Tier 1') ? 'High' : 'Normal'
               );    
 
          Date duedate = system.today().addDays((Integer)(contact.Contact_Frequency__c));
          newTask.ActivityDate = dueDate;
 
          newTasks.add(newTask);
     }
 
     if (!newTasks.isEmpty()) {
          insert newTasks;
     }
}

José Teixeira GomesJosé Teixeira Gomes
I think that does it. The only thing missing was the WhatId in the new task and the change you suggested. Thank you very much!
Next up, the test class. But that can wait for another day. How do I mark this as solved?