0

I am new to Salesforce Stack Exchange, I hope my question is relevant. I am creating a trigger handler (apex class) that auto generates a new task when certain conditions of the current task is met (basically, complete).

I am struggling with checking the conditions. The conditions I want to check are

  • The Field Create_New_Task__c is True
  • The record type ID is ABC
  • The related to WhatId is an account with the Class of A, B, or C
  • Status field changes to COMPLETE
public static void generateTask(object[] aTrigger) {
    List<Task> newTrigger = (List<Task>) aTrigger;
    for (Task t : newTrigger) {
        if (t.Create_New_Task__c == True &&
        t.RecordTypeId == ABC &&
        (t.WhatId == Account.Class == A || t.WhatId == Account.Class == B || t.WhatId == Account.Class == C ) &&
        ((t.Status != newTrigger.oldMap.get(t.Id).Status && t.Status == COMPLETE))
        )
         {}
    }
}

What am I doing wrong? I get an overwhelming amount of errors, including oldMap doesn't exist, comparison arguments must be compatible types: Id, Schema.SObjectField, variable does not exist (A, B, C),..etc.

The Trigger:

trigger TaskTrigger on Task (before insert , before update , before delete,after insert , after update , after delete)
{
    if (Trigger.isAfter) {
        System.debug(Trigger.newMap.values());
        TaskTriggerHandler.generateTask(Trigger.oldMap, Trigger.newMap);
    }
}
2
  • old map would be null in case of record insertion, make sure you add null check for trigger.old
    – gs650x
    Jul 12 at 13:31
  • It's not clear what what you mean by "the Class of Account". I've assumed that you're talking about a field in my answer. If that is incorrect, then you should edit your question to go into more detail about what you mean.
    – Derek F
    Jul 12 at 14:46
1

The errors you mentioned all seem to stem from some fundamental misunderstanding of what your code is really trying to do.

I think there are two main things that you've gotten wrong here

  • You aren't accessing related data from a given Task properly
  • You don't seem to understand how trigger context variables work

There are more issues with your code than that, but I think those two things are the main reason why your code isn't compiling.

Getting related data from Tasks is more involved than it is with most other SObjects because Tasks have polymorphic relationships (whoId and whatId). That is, the relationship field can point to one of a collection of different SObjects (normal relationship fields are tied to a single SObject).

The Task SObject has an AccountId field that is automatically populated in certain cases (if WhatId points to an Account, Opportunity, Contract, a Custom SObject that is "a child" of Account, or if WhoId points to a Contact). Generally speaking though, working with polymorphic relationships means you'll either be running a query to build a Map so you can access the related data, or using special syntax in your query for polymorphic relationships

Trigger context variables are what gives you the new and old data (as appropriate. For example, there isn't any "old" data when you're inserting a new record) when a trigger runs. Trigger.new and Trigger.old are simply Lists of SObjects. You can't access the values from Trigger.old directly from the data in Trigger.new, that's just not how Lists work. If you want both new and old data, your handler should have parameters for both.

A better/more correct version of your method would be

// Passing things in as a List<Object> just strikes me as odd
// Some trigger frameworks like to pass in Lists instead of Maps, but seeing as
//   how this code is only relevant on updates and in the interest of focusing on fixing
//   your misunderstandings instead of piling on more new things to learn, I think
//   maps are fine here
public static void generateTask(Map<Id, Task> oldMap, Map<Id, Task> newMap) {
    // Probably the most straightforward way to get related data from Tasks is to 
    //   run a separate query for your desired related object
    // What follows is the general "gather-query-map" approach
    Set<Id> recordIds = new Set<Id>();

    for(Task t :newMap.values()){
        // WhatId may or may not point to an account, but that's not really something
        //   we need to concern ourselves with here
        recordIds.add(t.WhatId);
    }

    // Class isn't a standard field on Account, so if it is a field, it must be 
    //   a custom field.
    // Thus, it requires '__c' at the end
    // At any rate, you need to query for related data, and this is where we're doing
    //   that
    Map<Id, Account> relatedAccounts = new Map<Id, Account>([SELECT Id, Class__c FROM Account WHERE Id IN :recordIds]);

    // Gathering your target Account class values beforehand can save some repetitive
    //   typing
    Set<String> targetAccountClasses = new Set<String>{'A', 'B', 'C'};

    // Now you're ready to do the main thing you're trying to accomplish
    for(Task t :newMap.values()){
        // Since this method takes both oldMap and newMap, you have access to the old
        //   data
        // Fetch it from oldMap based on the record id.
        Task oldTask = oldMap.get(t.Id);

        // <boolean condition> == true can always be reduced to just <boolean condition>
        if(
            t.Create_New_Task__c && t.RecordTypeId == '<some value>' 
            && targetAccountClasses.contains(relatedAccounts.get(t.WhatId)?.Class__c) 
            && oldTask?.Status != t.Status && t.Status == 'Complete'){
            // do work
        }
    }
}
13
  • Quick question as I'm reading through this, is newMap basically Trigger.new? What would I pass in my trigger class?
    – Carly
    Jul 12 at 14:47
  • @Carly newMap would be Trigger.newMap. You haven't showed us how this method is currently being called, but you'd likely call it like generateTask(Trigger.oldMap, Trigger.newMap);
    – Derek F
    Jul 12 at 14:50
  • Also, the ?. that I use in the if statement is the Safe Navigation Operator, which helps protect the code from running into Null Pointer Exceptions.
    – Derek F
    Jul 12 at 14:55
  • That's helpful, thank you! I am trying this out but am running into this error TaskTrigger: execution of BeforeInsert caused by: System.NullPointerException: Attempt to de-reference a null object Class.TaskTriggerHandler.generateTask: ... Looks like it's where it's iterating through newMap.values() for the first time to add to recordIds.
    – Carly
    Jul 12 at 15:04
  • I tried printing out the newMap with debug and it seems to be empty. Would you know why this is the case?
    – Carly
    Jul 12 at 15:09
0

Account.Class refers to the Type Schema.Account.sObjectType, not a specific account record. You can't get related record information directly in a trigger, you have to query for it first. Also, you shouldn't use Object for this case, you need to use the correct type for the trigger (e.g. Task). So, your code needs to start off with something like:

public static void generateTask(Map<Id, Task> beforeSaveTasks, Task[] aTrigger) {
Map<Id, Account> accounts = new Map<Id, Account>();
Id recordTypeId = sObjectType.Task.getRecordTypeInfosByDeveloperName().get('ABC').getRecordTypeId();
for(Task taskRecord: aTrigger) {
  accounts.put(taskRecord.AccountId, null);
}
accounts.putAll([SELECT Class__c FROM Account WHERE Id = :accounts.keySet()]);
for(Task taskRecord: aTrigger) {
  Account taskAccount = accounts.get(taskRecord.AccountId);
  ...

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.