+ Start a Discussion
DougWDDougWD 

Activity Completed Apex Trigger

Hi, I am new to apex trigger programming, though a long-time programmer.

I would like to track the date and time of day when tasks are marked "Completed". This functionality is not captured by Due Date or Date of Last Activity.

trigger DateCompleted on Task ( before insert, before update )
{
try
{
// task.DateCompleted = todaysDate();
// task.TimeCompleted = now;
}
catch ( Exception ex )
{
}
}

I'd appreciate your help on this - also (and related), I looked around for some time, but could not find a resource with a bunch of example Apex triggers - this would be really helpful.
Best Answer chosen by Admin (Salesforce Developers) 
DougWDDougWD
In case anyone else is trying to achieve this, here is the code that worked:
trigger DateCompleted on Task ( before insert, before update )
{
   Task[] checkTasks = Trigger.new;
   for(Task t : checkTasks){
      if(t.DateCompleted__c == NULL && t.Status=='Completed'){
         t.DateCompleted__c = System.today();
         t.DateTimeCompleted__c = System.now();
      }
   }
}
@isTest
private class testDCTrigger{

    public static testmethod void testme(){
        Account acc = new account(name = 'TestCoverage');
        insert acc;
        Task testtask = new task(whatID = acc.id, subject = 'Task A.1', status = 'Not Started', priority = 'Normal');

        // Task t = new Task(name='TestTask');
         String taskid;
         testtask.status='Completed';
         try{
            insert testtask;
            taskid=testtask.id;
         }catch(DMLException e){
            system.assert(false,'Error Inserting Test Task:'+e.getDMLMessage(0));
         }
         Task checktesttask = [select id,Status,DateCompleted__c,DateTimeCompleted__c from Task where id=:taskid];
         system.assertequals('Completed',checktesttask.Status); //verify task still marked completed
         
         //verify current system time less that 15000 ms difference from inserted data, may need to adjust
         system.assert((system.now().getTime()- checktesttask.DateTimeCompleted__c.getTime())<15000);    
         system.assert((system.today() == checktesttask.DateCompleted__c));    

    }
}
Now to figure out how to migrate from sandbox to production.  Thanks so much Jim for your help!
Doug Claffey
WorkplaceDynamics
 

All Answers

JimRaeJimRae
There are lots of good examples here on these discussion boards, and in the documentation, and in the cookbook:
Look here: http://wiki.apexdevnet.com/index.php/Documentation

Your trigger seems pretty straightforward.  Assuming you created a custom field to hold the completed datetime, it would look something like this:

Code:
trigger DateCompleted on Task ( before insert, before update )
{
   Task[] checkTasks = Trigger.new;
   for(Task t : checkTasks){
      if(t.Status=='Completed'){
         t.DateCompleted__c=System.now();//Assumes the name of the new field is DataCompleted and is a DateTime DataType
      }
   }
}

You will also need to create a Testmethod to test the trigger before it can be deployed to production.  Both the trigger and the testmethod will need to be deployed.
Here is a simple testmethod:


Code:
@isTest
public class testDCTrigger{

    public static testmethod void testme(){
         Task testtask = new Task(name='Test Task');
         String taskid;
         testtask.status='Completed';
         try{
            insert testtask;
            taskid=testtask.id;
         }catch(DMLException e){
            system.assert(false,'Error Inserting Test Task:'+e.getDMLMessage(0));
         }
         Task checktesttask = [select id,Status,DateCompleted__c from Task where id=:taskid];
         system.assertequals('Completed',checktesttask.Status); //verify task still marked completed
         
         //verify current system time less that 10000 ms difference from inserted data, may need to adjust
         system.assert((system.now().getTime()-checktesktask.DateCompleted__c.getTime())<10000);
         

    }
}

 
This is all prototype code, it may need a little tweaking to get working in your environment
Good Luck!!!
DougWDDougWD
Thanks for the quick response. I had come up with something similar, but am now stumped accessing the custom field. Here is the trigger code thus far.

Trigger DateCompleted on Task (before update, before insert) {
for (Task t : Trigger.new) {
}
if(t.DateCompleted__c == NULL && t.IsClosed)
{
t.DateCompleted__c = date.today();
t.DateTimeCompleted__c = date.now();
}
}

I had created the t.DateCompleted__c and t.DateTimeCompleted__c fields as custom Activity fields in my sandbox. I recieved the error:

Variable does not exist: t.DateCompleted__c at line 6 column 9

Do I need to do something special to get to a custom activity field from a task (tasks don't seem to have their own custom fields). Thanks!
JimRaeJimRae
You shouldn't have to do anything special to access the custom activity field on your tasks, are you sure you have the correct API name for the field?
IF you called it Date Completed when you created it, the API name would be Date_Completed__c.  Go back to the setup, and select the field you created in the Custom Activity Fields.  It will show you what the API name is.
DougWDDougWD
I had copy pasted the name right out of the activity->custom fields dialog, after API Name.
 
I have two theories:
 
1) is there an issue that the custom field applies to the Activity, rather than specifically to the Task?
2) could it have to do with the fact that I added the custom field to the sandbox version of my data?
 
Otherwise, I am stumped.
 
Thanks in advance.
 
Here is the screen shot:
 

Field Definition Detail

 
Error: Invalid Data.
Review all error messages below to correct your data.

Field Information:

Field LabelDateCompletedObject NameActivity
Field NameDateCompletedData TypeDate
API NameDateCompleted__c  
DescriptionDate activity was marked completed
Help Text
Created ByDoug, 1/13/2009 2:28 PMModified ByDoug 1/13/2009 2:28 PM
JimRaeJimRae
One last thought, before you give up!
Did you set the field level security so that the new field was visible (to at least your profile) and not read only?  Ultimately it will need to be visible to any profile that could cause the trigger to fire.
DougWDDougWD
Pasted in your code verbatim and the custom field worked fine.  Still unsure what I was doing wrong, but will press on - thanks for your help!
DougWDDougWD
In case anyone else is trying to achieve this, here is the code that worked:
trigger DateCompleted on Task ( before insert, before update )
{
   Task[] checkTasks = Trigger.new;
   for(Task t : checkTasks){
      if(t.DateCompleted__c == NULL && t.Status=='Completed'){
         t.DateCompleted__c = System.today();
         t.DateTimeCompleted__c = System.now();
      }
   }
}
@isTest
private class testDCTrigger{

    public static testmethod void testme(){
        Account acc = new account(name = 'TestCoverage');
        insert acc;
        Task testtask = new task(whatID = acc.id, subject = 'Task A.1', status = 'Not Started', priority = 'Normal');

        // Task t = new Task(name='TestTask');
         String taskid;
         testtask.status='Completed';
         try{
            insert testtask;
            taskid=testtask.id;
         }catch(DMLException e){
            system.assert(false,'Error Inserting Test Task:'+e.getDMLMessage(0));
         }
         Task checktesttask = [select id,Status,DateCompleted__c,DateTimeCompleted__c from Task where id=:taskid];
         system.assertequals('Completed',checktesttask.Status); //verify task still marked completed
         
         //verify current system time less that 15000 ms difference from inserted data, may need to adjust
         system.assert((system.now().getTime()- checktesttask.DateTimeCompleted__c.getTime())<15000);    
         system.assert((system.today() == checktesttask.DateCompleted__c));    

    }
}
Now to figure out how to migrate from sandbox to production.  Thanks so much Jim for your help!
Doug Claffey
WorkplaceDynamics
 
This was selected as the best answer
Praetorian65Praetorian65

Too easy. Im suprised everyone missed this:

Code:
Trigger DateCompleted on Task (before update, before insert) {
for (Task t : Trigger.new) {
}
if(t.DateCompleted__c == NULL && t.IsClosed)
{
t.DateCompleted__c = date.today();
t.DateTimeCompleted__c = date.now();
}
}


 
Look at the bit in bold. "t" only exists within the "for" segment.

srichardsonsrichardson

Here is a workaround that does not require an Apex Trigger. You can create a workflow rule that fires an immediate action field update to update a custom field.

 

Evaluation Criteria: Every time a record is created or edited

Rule Criteria: NOT(ISPICKVAL(PRIORVALUE (Status), "Completed")) && ISPICKVAL(Status, "Completed")

Field Update: set custom field "Date Completed" = NOW()

 

This seems to be working great for me and requires no Apex triggers and test methods.

DougWDDougWD

Great solution - thanks!