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
mhamberg1mhamberg1 

bulk lead trigger error - Attempt to de-reference a null object

I'm writing a bulk trigger to update lead owners based on a zip code table we have.

I think I'm close to having the whole thing working, but I'm getting the "Attempt to de-reference a null object" error when I actually execute a test of my code.

Here is the code:

Code:
trigger LeadAssignmentTrigger on Lead (before insert) 
{    
    List<String> zips = new List<String>();

    // Loop through Leads and create list of zip codes
    for (Lead lead : Trigger.new)
    {
        if (lead.PostalCode != NULL)
        {
            zips.add(lead.PostalCode);    
        }
    }

    //query zip code table and link zips to Sales reps
    // Name field = a single zip code
Map<String, Zip_Code__c> leadsToUpdate = new Map<String, Zip_Code__c>
([select Name, Sales_Rep__c from Zip_Code__c where Name in :zips]);

// If there is at least one match, bulk update
if (leadsToUpdate.size() > 0)
{
for (Lead lead : Trigger.new)
{
//assign the lead owner to the zip code owner
lead.OwnerId = leadsToUpdate.get(lead.PostalCode).Sales_Rep__c; //this line gives the error
}
}
}

There must be some small problem I have in here. Basically what I want to do is find the Sales Rep who corresponds to the new lead's zip code and assign that sales rep as the new lead OWNER.

If anyone knows why I'm getting that error and can give me some advice I would be much obliged.

Thanks

Best Answer chosen by Admin (Salesforce Developers) 
mhamberg1mhamberg1
Thanks everyone. After enough pounding it into my head, I finally figured it out!

I was thinking that because you had a query in the for loop that it wouldn't be bulk loadable. But what I missed is that the query only has to happen once in that scenario.

For all the other Map newbies out there, once I finally understood what Simon was saying above, everything was clear as day: you can still use a SOQL query to figure out what your Map is, but you have to loop through and setup the Key yourself or otherwise the query will automatically assign the object ID as your key and that's probably not what you want.

Here is my working code to reassign (in bulk, if necessary) the lead owner id to a more appropriate one based on their zip code. Hope this is helpful:

Code:
trigger LeadAssignmentTrigger on Lead (before insert) 
{    
    List<String> zips = new List<String>();

    // Loop through Leads and create list of zip codes
    for (Lead lead : Trigger.new)
    {
        if (lead.PostalCode != NULL)
        {
            zips.add(lead.PostalCode);    
        }
    }

    //query zip code table and link zips to Sales reps
    Map<String, Zip_Code__c> leadsToUpdate = new Map<String, Zip_Code__c>();

    for(Zip_Code__c zip_code:[select Name, Sales_Rep__c from Zip_Code__c where Name in :zips]) 
    { 
       if(zip_code.Name != null) leadsToUpdate.put(zip_code.Name, zip_code);
    } 


    // If there is at least one match, bulk update
    if (leadsToUpdate.size() > 0)
    {
        
        for (Lead lead : Trigger.new)
        {
            // only update if the lead zip code exists in the Map
            if (leadsToUpdate.containsKey(lead.PostalCode))
            {
              //assign the lead owner to the zip code owner
              lead.OwnerId = leadsToUpdate.get(lead.PostalCode).Sales_Rep__c;    
            }
        }
          
     }     
}

 

All Answers

SuperfellSuperfell
You have a lead with a postalCode value that doesn't have a row in the zip_code__c table.
SuperfellSuperfell
Oh, its not that, the leadsToUpdate map is keyed on the id of the zip_code__c object, not the postalCode, so that map lookup will always return null if you're not passing it an id.
JimRaeJimRae
When you go through your trigger array the first time, you take into account the possibility that the Postal_Code might be null, but in your update loop, you do not.  The first lead passed in with no postal code would cause the error.
 
You might want to do the checking twice, or create a list to update in the first place.
 
Try this:
 
 
Code:
trigger LeadAssignmentTrigger on Lead (before insert) 
{    
    List<String> zips = new List<String>();
    List<Lead> updLeads = new List<Lead>();

    // Loop through Leads and create list of zip codes
    for (Lead lead : Trigger.new)
    {
        if (lead.PostalCode != NULL)
        {
            zips.add(lead.PostalCode);
            updLeads.add(lead);    
        }
    }

    //query zip code table and link zips to Sales reps
    // Name field = a single zip code
    Map<String, Zip_Code__c> leadsToUpdate = new Map<String, Zip_Code__c>
    ([select Name, Sales_Rep__c from Zip_Code__c where Name in :zips]);
    // If there is at least one match, bulk update    
    if (leadsToUpdate.size() > 0)    
    {
        for (Lead lead : updLeads)
        {
              //assign the lead owner to the zip code owner
              lead.OwnerId = leadsToUpdate.get(lead.PostalCode).Sales_Rep__c; //this line gives the error
        }
    }
} 

 
mhamberg1mhamberg1
Simon, I'm not quite sure I understand what you mean by "the leadsToUpdate map is keyed on the id of the zip_code__c object, not the postalCode" - if I understand map keys, it's the first value in the pairing which is the key. In this case my key is a String and it's the Zip Code match from the select statement (basically my initial List).

Is that wrong?

Jim, thanks for your tip - I'll add in another check to make sure I handle null values once I get it working.
mhamberg1mhamberg1
Can anyone else chime in on this? I don't see how the get() function I have doesn't work. Or maybe the null is coming because the Map isn't filled in properly?

Any advice would be appreciated.

Thanks
SuperfellSuperfell
dump your map to the system log, you'll see the map key is the record Id. e.g. run this from the system log window

map<String, Account> acc = new map<String, Account>( [select name, accountNumber from account limit 5]);
System.debug(acc);

in the log you'll see something like
09:05:57 INFO - 20081202170557.318:AnonymousBlock: line 1, column 1: Anonymous
20081202170557.318:AnonymousBlock: line 1, column 5: DeclareVar: MAP:String,SOBJECT:Account acc
20081202170557.318:AnonymousBlock: line 1, column 54: SOQL query with 5 rows finished in 8 ms
20081202170557.318:AnonymousBlock: line 1, column 5: initial value: {0018000000MLx2VAAT=Account:{AccountNumber=CC978213, Name=GenePoint, Id=0018000000MLx2VAAT}, 0018000000MLx2WAAT=Account:{AccountNumber=CD355119-A, Name=United Oil & Gas, UK, Id=0018000000MLx2WAAT}, 0018000000MlWi0AAF=Account:{AccountNumber=r123, Name=rotor inc., Id=0018000000MlWi0AAF}, 0018000000Mm4QsAAJ=Account:{AccountNumber=JR1, Name=John's Rotors, Id=0018000000Mm4QsAAJ}, 0018000000Mn7AhAAJ=Account:{Name=Orcsweb, Id=0018000000Mn7AhAAJ}}
20081202170557.318:AnonymousBlock: line 2, column 1: System.debug(MAP:String,SOBJECT:Account)
20081202170557.318:AnonymousBlock: line 2, column 1: {0018000000MLx2VAAT=Account:{AccountNumber=CC978213, Name=GenePoint, Id=0018000000MLx2VAAT}, 0018000000MLx2WAAT=Account:{AccountNumber=CD355119-A, Name=United Oil & Gas, UK, Id=0018000000MLx2WAAT}, 0018000000MlWi0AAF=Account:{AccountNumber=r123, Name=rotor inc., Id=0018000000MlWi0AAF}, 0018000000Mm4QsAAJ=Account:{AccountNumber=JR1, Name=John's Rotors, Id=0018000000Mm4QsAAJ}, 0018000000Mn7AhAAJ=Account:{Name=Orcsweb, Id=0018000000Mn7AhAAJ}}


See how the map keys are the Id of the account.

Message Edited by SimonF on 12-02-2008 09:07 AM
mhamberg1mhamberg1
Ok, I see what you're saying but I don't understand how to apply that to my code.

If I have a map that looks like this:

< Zip Code, Sales Rep>
< 44333, Jim Smith>
< 44222, Mike Johnson>

How can I loop through my newly created Leads and assign the right zip code to the sales rep? My lead object has no clue what the IDs are of the Zip Code object.  I thought that was the point of using the Map's get() function and setting up a map in the first place.

Thanks


JimRaeJimRae
I think I see what your issue is. You are trying to update the OwnerID field with the name of the rep.  You need the UserID, not the name.  You either need to change your zip table to include the OwnerID instead of the reps name, or you will need to do a lookup that maps them back to the id (which could be dangerous from a bulk safe perspective, since you would need a lookup for each object you are trying to insert).
mhamberg1mhamberg1
My zip code object already has a zip code and a User lookup field. I was just using the map above as an example to help me understand how I should be calling the map.
Simon says I need to somehow reference the ID of the map instead of being able to use the Zip code string as the ID and do a lookup on that.

My question is how do I do that? As I loop through my Leads being inserted, I only know what the zip codes are of each lead, I can easily map that zip to a sales rep based on my zip code object, but I'm having trouble doing the final assignment of the Sales Rep ID (User lookup) to the Lead Owner ID.

Does anyone know what I'm doing wrong here?
JimRaeJimRae
You need to have the actual SFDC User id for the sales rep, not their name.  The apex code in the trigger will need to behave like the lookup does, you see the name, the system expects the id.
 
So, if you look at your Zip_Code__c object records and the fields you have are like this:
String Name (which represents the zip code)
String Sales_Rep__c (which represents the name of the Rep)
 
With a record that looks like this
 
90210, Brandon Walsh
you won't get your desired effect.
You need the userid field, so the record looks like this:
90210, 00570000000mbXX
You should also consider putting in some error handling to cover the condition where the postalcode doesn't exist in the map, so you don't try to update with a null
Code:
trigger LeadAssignmentTrigger on Lead (before insert) 
{    
    List<String> zips = new List<String>();
    List<Lead> updLeads = new List<Lead>();

    // Loop through Leads and create list of zip codes
    for (Lead lead : Trigger.new)
    {
        if (lead.PostalCode != NULL)
        {
            zips.add(lead.PostalCode);
            updLeads.add(lead);    
        }
    }

    //query zip code table and link zips to Sales reps
    // Name field = a single zip code
    // Sales_Rep__c field = SFDC User id of sales rep

    Map<String, Zip_Code__c> leadsToUpdate = new Map<String, Zip_Code__c>();
 //to make sure the Map is getting set up correctly
    for(Zip_Code__c z :[select Name, Sales_Rep__c from Zip_Code__c where Name in :zips]){
 leadsToUpdate.put(z.name,z);
 }



    // If there is at least one match, bulk update    
    if (leadsToUpdate.size() > 0)    
    {
        for (Lead lead : updLeads)
        {
              //assign the lead owner to the zip code owner
              Zip_Code__c updzip = leadsToUpdate.get(lead.PostalCode);
  if(updzip!=null){
               lead.OwnerId = updzip.Sales_Rep__c;
  }else{
   //handle the error condition here
   system.debug('Postal Code: '+updzip.name +' did not exist in map');
  }

        }
    }
} 

 
 
mhamberg1mhamberg1
What I was trying to say in my previous post is that is exactly what we already have in our zip code object.

<90210, UserID (such as 00570000000mbXX)>

So my table looks like that but I'm still trying to decipher what Simon said about how I can't do the lookup using the zip code, I have to use the UserID key or something like that. It doesn't make sense to me based on all the of Map documentation I've read.

Can anyone assist in this and tell me why this line of code doesn't work?

Code:
//assign the lead owner to the zip code owner
 lead.OwnerId = leadsToUpdate.get(lead.PostalCode).Sales_Rep__c;


 Thanks


SuperfellSuperfell
You can do the map lookup on any key you like, but you have to build the map with those keys, you're using the SOQL support for building the map, and that always uses the Id as the key. So you need to not do that, and instead read the query results yourself and build the map with the key of your choice.
BritishBoyinDCBritishBoyinDC
I think this post might help - sounds like the map needs to be created slightly differently to achieve your goal:

http://community.salesforce.com/sforce/board/message?board.id=apex&message.id=342#M342
JimRaeJimRae
In the sample I posted back for you, I suggested the same thing. Use a for loop to create your map, to insure that you are getting the correct index and value.
Code:
//to make sure the Map is getting set up correctly
    for(Zip_Code__c z :[select Name, Sales_Rep__c from Zip_Code__c where Name in :zips]){
 leadsToUpdate.put(z.name,z);
 }


 
Try that and see if you have better results.
My only other suggestion would be to add some debug statements and look at the output when you execute your testmethod.
something like this:
 
Code:
 if (leadsToUpdate.size() > 0)    
    {
        for (Lead lead : updLeads)
        {
             system.debug('LEAD: '+lead);
             system.debug(POSTALCODE: '+lead.PostalCode);
             system.debug(USER ID Retrieved: '+leadsToUpdate.get(lead.PostalCode));
              
              //assign the lead owner to the zip code owner
              Zip_Code__c updzip = leadsToUpdate.get(lead.PostalCode);
  if(updzip!=null){
               lead.OwnerId = updzip.Sales_Rep__c;
  }else{
   //handle the error condition here
   system.debug('Postal Code: '+updzip.name +' did not exist in map');
  }


 
Post the output of the system log when you execute your test if you still have problems.
mhamberg1mhamberg1
Thanks everyone. After enough pounding it into my head, I finally figured it out!

I was thinking that because you had a query in the for loop that it wouldn't be bulk loadable. But what I missed is that the query only has to happen once in that scenario.

For all the other Map newbies out there, once I finally understood what Simon was saying above, everything was clear as day: you can still use a SOQL query to figure out what your Map is, but you have to loop through and setup the Key yourself or otherwise the query will automatically assign the object ID as your key and that's probably not what you want.

Here is my working code to reassign (in bulk, if necessary) the lead owner id to a more appropriate one based on their zip code. Hope this is helpful:

Code:
trigger LeadAssignmentTrigger on Lead (before insert) 
{    
    List<String> zips = new List<String>();

    // Loop through Leads and create list of zip codes
    for (Lead lead : Trigger.new)
    {
        if (lead.PostalCode != NULL)
        {
            zips.add(lead.PostalCode);    
        }
    }

    //query zip code table and link zips to Sales reps
    Map<String, Zip_Code__c> leadsToUpdate = new Map<String, Zip_Code__c>();

    for(Zip_Code__c zip_code:[select Name, Sales_Rep__c from Zip_Code__c where Name in :zips]) 
    { 
       if(zip_code.Name != null) leadsToUpdate.put(zip_code.Name, zip_code);
    } 


    // If there is at least one match, bulk update
    if (leadsToUpdate.size() > 0)
    {
        
        for (Lead lead : Trigger.new)
        {
            // only update if the lead zip code exists in the Map
            if (leadsToUpdate.containsKey(lead.PostalCode))
            {
              //assign the lead owner to the zip code owner
              lead.OwnerId = leadsToUpdate.get(lead.PostalCode).Sales_Rep__c;    
            }
        }
          
     }     
}

 

This was selected as the best answer
JohnC5674JohnC5674
Maybe someone can help me.  I am trying to do what mhamberg1 did and I am getting the error of Loop variable must be of type SObject.
 
Here is the code.
 
trigger LeadAssignmentTrigger on Lead (before insert) 
{    
    List<String> zips = new List<String>();

    // Loop through Leads and create list of zip codes
    for (Lead lead : Trigger.new)
    {
        if (lead.PostalCode != NULL)
        {
            zips.add(lead.PostalCode);    
        }
    }

    //query zip code table and link zips to Sales reps
    Map<String, Zip_Code__c> leadsToUpdate = new Map<String, Zip_Code__c>();

    for(Zip_Code__c zip_code:[select Name, Owner.Id from Zip_Code__c where Name in :zips]) 
    { 
       if(zip_code.Name != null) leadsToUpdate.put(zip_code.Name, zip_code);
    } 


    // If there is at least one match, bulk update
    if (leadsToUpdate.size() > 0)
    {
        
        for (Lead lead : Trigger.new)
        {
            // only update if the lead zip code exists in the Map
            if (leadsToUpdate.containsKey(lead.PostalCode))
            {
              //assign the lead owner to the zip code owner
              lead.OwnerId = leadsToUpdate.get(lead.PostalCode).Owner.ID;    
            }
        }
          
     }     
}
mhamberg1mhamberg1
Hi John, on what loop are you getting that error?
JimRaeJimRae

John,

There are only 2 loop types in your code (3 loops total).  Two of them are on the Lead object,which is standard and must exist.

The other is on the Zip_Code__c custom object.  Does that exist?  Is it the correct API Name?