4

Trying to build what I thought would be a simple trigger: when a task is created that is only related to a contact (WhoId = contact type, but WhatId = null), relate the task's WhatId to the contact's account. I know I can't just set the WhatId equal to AccountId without querying for it (and that in an after trigger), so I wrote the following query, which is saving without error.

However, once I save the trigger and create a test Task in my sandbox, I get a null pointer exception. It's throwing here on line 22, column 1 -- where I set the WhatId = c.AccountId.

Can someone point out what I'm missing here?

trigger addAccountToTask on Task (after insert) {

Set<Id> taskWhoIds = new Set<Id>();

for (Task t : Trigger.new){
    if(t.WhoId != null){
        if(((String)t.WhoId).startsWith('003')){
                taskWhoIds.add(t.WhoId);}
        }
    }

    List<Contact> aIdList = [SELECT AccountId, Id FROM Contact WHERE Id IN :taskWhoIds];

    Map<Id, Contact> whoToAccountMap = new Map<Id, Contact>();
    for (Contact who : aIdList) {
        whoToAccountMap.put(who.AccountId, who);
    }

    for (Task t : Trigger.new){
        Contact c = whoToAccountMap.get(t.whoId);
        if(t.WhatId == null){
            t.WhatId = c.AccountId;
        }
    }
}

Much appreciated!

// Edited to include fixed trigger. Thank you @crmprogdev for all your help! //

This trigger is working just as I'd hoped! Hopefully this helps anyone else looking for this.

trigger addAccountToTask on Task (after insert) {

Set<Id> taskWhoIds = new Set<Id>();
for (Task t : Trigger.new){
    if(t.WhoId != null){
        if(((String)t.WhoId).startsWith('003')){
                taskWhoIds.add(t.WhoId);}
            }
        }

    List<Contact> aIdList = [SELECT Id, AccountId FROM Contact WHERE Id IN :taskWhoIds];

    Map<Id, Id> whoToAccountMap = new Map<Id, Id>();
    for (Contact who : aIdList) {
        if(who.AccountId != null){
           whoToAccountMap.put(who.Id, who.AccountId);
        }   
    }
    List<Task> taskList = new List<Task>();
    for (Task t : Trigger.new){
        Task toUpdate = new Task (Id = t.Id);
        Id aId = whoToAccountMap.get(t.whoId);

        if(aId != null){
            if(t.WhatId == null){
                toUpdate.WhatId = aId;
                taskList.add(toUpdate);
                }
            }
        }
        update taskList;
    }
3

You're making the assumption that your query returned an AccountId for every ContactId in your query. You didn't check to see if who.AccountId = null in the following loop:

for (Contact who : aIdList) {
    whoToAccountMap.put(who.AccountId, who);
} 

You needed to add an if statement as below:

for (Contact who : aIdList) {
    if(who.AccountId != null) whoToAccountMap.put(who.AccountId, who);
} 

You'll also need to check further down to make certain the map contains the whoId as well.

  • thanks, @crmprogdev -- I don't doubt your logic, but isn't this unnecessary if the master-detail relationship of contacts to accounts means every contact needs an account? I'm still new to apex, so I know I often make assumptions in my code when I think something is implied -- but this is one I really didn't think I needed to include! Same thing with checking later -- why do I check again? Isn't the map inherently made up of the WhoId(s)? – mlpSFadmin Jul 2 '15 at 3:27
  • Sorry for what are all such beginner questions -- I'm coming at this with no background in code! – mlpSFadmin Jul 2 '15 at 3:27
  • If the first test above fails, the key and value won't be put to the whoToAccountMap, and therefore can't be retrieved later when you try to execute Contact c = whoToAccountMap.get(t.whoId); in the final for loop since t.whoId won't be contained in the map. That's the source of the null pointer exception and we're removing it. You might say it's a data error someplace between accounts and contacts. – crmprogdev Jul 2 '15 at 3:45
  • thanks @crmprogdev -- that's helpful! I added it and it did get rid of the error! The trigger isn't working, though - so if you have any tips there, greatly appreciated. I believe I need to create a new stackexchange post to ask that question, though, as this one has technically been resolved. – mlpSFadmin Jul 2 '15 at 18:33
  • The trigger isn't working because 1. you've not added code to first sort out only the Ids in Trigger.new that don't also have a What.Id in addition to a Who.Id. And 2. Once you've added in the values for the Account WhatId to those Tasks with the AccountIds, you've not added them to a list and performed a DML statement after your last For loop as in: Update TaskLst. You need to create a new task where Id= t.Id and add it to a list in that loop. – crmprogdev Jul 2 '15 at 20:57
0

Whenever t.whoId is not a contact (does not start with 003 as you have tested for at the top), then you won't get a contact - you'll get null.

All you have to do is check that your fetched contact is not null and your error will go away.

Add this after you retrieve the contact:

Contact c = whoToAccountMap.get(t.whoId);
if (c == null){
    continue;
}

UPDATE You may also want to disregard whether WhatId is empty - you want to overwrite no matter what, I would have thought. So take out this:

if(t.WhatId == null)

As this will prevent you overwriting that value if something is already there.

  • Thanks @CaspNZ -- I added the line you suggested, though I changed it to if (c != null){ continue; } because I think that was what you really suggesting -- it got rid of the null pointer exception, so that helps! Upon save, the task still isn't related to the Account, so clearly I'm missing something else, but that's one hurdle down at least. Didn't think I'd need to check that when I was already checking the WhoId starts with 003 (thought being null values can't start with anything), but I guess not! – mlpSFadmin Jul 2 '15 at 3:17
  • You can also set OwnerId to assign the task to someone. – Caspar Harmer Jul 2 '15 at 5:18
  • Thanks @caspNZ -- assigning is not necessary, though. I'm just looking for an a way to avoid reps needing to physically attach things to accounts for reporting. – mlpSFadmin Jul 2 '15 at 18:29
  • I've updated the answer with a suggestion that may help. – Caspar Harmer Jul 2 '15 at 19:09
  • No worries! I actually do not want to overwrite -- these tasks are occasionally linked to opportunities, which is preferable to account when applicable. If I wanted to get really fancy, I'd check to make sure it's not attached to one of our main custom objects, but that happens so rarely I don't find it worth including in the code! – mlpSFadmin Jul 2 '15 at 19:11

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.