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
MickleMickle 

Apex Trigger to update lookup field, need help

Alright, I have very little experience coding with Apex (or any other programming language for that matter), however I am generally alright at modifying code. However, there is an area I am having trouble with, and even though I know a lot of people have had a very similar problem, I cannot seem to fix this.

 

I have a custom object created, that I am trying to link to the Lead object, using a lookup field. I have it set up perfectly manually, however I want to automatic this process using Apex, I get an error message.

 

Lead Object Fields:
PostalCode 
ACZipCode API Name:ACZipCode__c Type: Lookup(ACZipCode)

 

Custom Object ACZipCode fields API Name: ACZipCode__c

ZipCode

API Name:ZipCode__c

Type:Text(5)

 

Apex Trigger needing work:

trigger GetZipCode on Lead (after insert, after update) {
for (Lead a : Trigger.new) {a.ACZipCode__c = a.PostalCode; }}

 

Error message:

Review all error messages below to correct your data.
Apex trigger GetZipCode caused an unexpected exception, contact your administrator: GetZipCode: execution of AfterUpdate caused by: System.StringException: Invalid id: 43225: Trigger.GetZipCode: line 2, column 29

 

I believe what it is doing is passing the raw zipcode number though, when it really needs the ACZipCode ID number. I just don't know how to fix this. Again, everything works perfectly if I disable the Apex trigger, and just manually type in the 5 digit zipcode (I'm guessing it does the lookup behind the scenes).

 

Any help would be appreciated, thanks!

 

 

ngabraningabrani

If you look at this statement -

a.ACZipCode__c = a.PostalCode;

 

PostalCode is of type String. ACZipCode__c is a lookup to a custom object. So when the assignment happens, the right hand side is a five digit zip code. The assignment will only work if you assign a valid ID to zipcode object.

 

You will need to get a SOQL query on the Zipcode object. This query will use PostalCode as an input and will get the object id. This id can then be assigned to a.ACZipCode__c.

 

MickleMickle

Thanks, I figured as much. The problem is with my limited knowledge of Apex, I'm not exactly sure how to code this.

 

Can you offer any additional pointers?

MickleMickle

Can anyone review this code, and help me see if there are any glaring vulnerabilities for failure, or room for improvement? I'm very new to coding (do my ugly variable names make it obvious? :P ), so even though this works, and I've tested it via manual entry, manual changing, and mass uploading via the data loader, I'm still not 100% ready to push towards production.. 

 

Key (Since some things changed from original post)

 

Lead Object

left5zip__c = custom field, basically if len(PostalCode >4) then it's the left 5 digits

mikezip__c = custom lookup field to the ZipCodeTable__c object

 

ZipCode Object

ZipCodeID__c = 5 digit zip code

ZipCode ID a0YQ0000002vg4 is ID for the record on the Zipcode Object for unknown ZipCodes

ZipCode ID a0YQ0000002wKD1  in my test class is the ID for the record on the Zipcode Object for Zip Code 98026. Would be better if this wasn't hard coded...but I got tired.

 

 

 

 

trigger getzip3 on Lead (before update, before insert) {
    //set for holding unique left5zip
    Set<String> left5zips = new Set<String>();    
    //get left5zip from each lead
    for (lead l : trigger.New) {
        String left5zip = l.left5zip__c;
        left5zips.add(left5zip);
    }
    Map<String<left5zip>, Id> zmap = new Map<String<left5zip>, Id> ();
    List<ZipCodeTable__c> zipcodeIdlist = [SELECT ZipCodeID__c, Id from ZipCodeTable__c WHERE ZipCodeID__c IN :left5zips];
    
    for (ZipCodeTable__c z : zipcodeIdlist) {
        zmap.put (z.ZipCodeID__c, z.Id); 
    }
       
   
    for (lead l : trigger.New) {
        String left5zip = l.left5zip__c;
        
        if (zmap.containsKey(left5zip)) {
            String key = zmap.get(l.left5zip__c);
            l.mikezip__c = key;
        }
        else {
            String key = 'a0YQ0000002vg4r';
            l.mikezip__c = key; 
        }
    }
   }

 This is the quick test class I wrote, and it passes with 100% coverage, but obviously that doesn't mean everything.

 

 

@isTest
private class TestZip {
    static testMethod void testZipChange() {
        lead[] leadtest = new lead[]{
         new lead(FirstName ='mike', LastName = 'Smith', Company = 'lolz', PostalCode = '98020'),
         new lead(FirstName ='fred', LastName = 'Smith', Company = 'lolz', PostalCode = '68516'),
         new lead(FirstName ='george', LastName = 'Smith', Company = 'lolz', PostalCode = '68517'),
         new lead(FirstName ='steve', LastName = 'Smith', Company = 'lolz', PostalCode = '98105'),
         new lead(FirstName ='mike', LastName = 'Smith', Company = 'lolz', PostalCode = '98106'),
         new lead(FirstName ='betty', LastName = 'Smith', Company = 'lolz', OwnerId = '0054000000131ru', PostalCode = '555555')
         };
         insert leadtest;
            
        Test.startTest();
        leadtest[0].PostalCode = '98026';
        update leadtest;
        Test.stopTest();
    
        leadtest = [SELECT id, PostalCode, mikezip__c FROM lead WHERE id IN:leadtest];
       
        system.assert(leadtest[0].mikezip__c == 'a0YQ0000002wKD1');
    }
}

 

 

SteveBowerSteveBower

Hi, Well, there are a few things I see....

 

First, it's hard to understand what you're trying to do without Comments.   Comments are important.  :-) 

 

Second, hard coding ID's is bad.  An Id that you've hardcoded and works perfectly in a Sandbox won't work in production.  And, it's just bad form.

 

Third the "special record" in the ZipCodeTable__c object for "unknown zip code" is a bad practice.  Why not just leave milezip__c as null if there is no match?

 

This is what I'd have written:

 

 

// For each lead in the trigger, if left5Zip__c is not null, use it to find a record in 
// the ZipCodeTable__c object with a matching ZipCodeID__c field.  
// Then use that Id to populate the mikezip__c field on the lead.  
// If there is no match, then set mikezip__c to null (which may require clearing it out.)

trigger getzip3 on Lead (before update, before insert) {
    // Build a set of the Zip's we're searching for.
    Set<String> left5zips = new Set<String>();    
    for (lead l : trigger.New) if (l.left5zip__c != null) left5zips.add(l.left5zip__c);

    // We may have no zips to process
    if (left5zips.isEmpty()) return;
    
    // Retrieve the matching ZipCodeTable__c records for the Zip's we want and 
    // insert it into a Map to map a Zip to the Id.
    Map<String, Id> zmap = new Map<String, Id> ();
    for (ZipCodeTable__c z : 
        [SELECT ZipCodeID__c, Id from ZipCodeTable__c WHERE ZipCodeID__c IN :left5zips])                  
            zmap.put (z.ZipCodeID__c, z.Id); 
       
   
    for (lead l : trigger.New) 
       // Clear out any existing value. (perhaps there was one from the initial insert, but
       // regardless of what it may have been, we're resetting it now).
       l.mikezip__c = null;
  
       if (zmap.containsKey(l.left5zip__c)) l.mikezip__c = zmap.get(l.left5zip__c);
   }

 

 

Your test case only tests the update condition, not the insert condition.  And then there's update from a case where there was an existing mikezip__c value and when there isn't.

 

And, what if an array of Leads has been updated or inserted.  What about cases where there *is* a matching code in the ZipCodeTable, and when there isn't.

 

So, there's a bit more to test.   Best, Steve.

 

 

 

 

 

 

MickleMickle

Steve,

 

Awesome! Thanks for the feedback. I'm sure it's painfully obvious how new to coding I am, so I love the feedback.  I totally forgot about ID's changing from Sandbox to Production....I knew it was bad form...but now I understand how it's really really bad form. :)

 

Will definitely take a look at this and start to wrap my head around it. The good thing is I definitely error on the safe side with regards to pushing anything through to production. 

SteveBowerSteveBower

Hi, and, it just occurred to me that in the update case, you don't need to bother looking at the lead unless the value for the Zip in trigger.old is different from the one in trigger.new.   So, you should check that in the If statement before bothering to add that zipcode to the set.  

 

if (trigger.isUpdate and l.zipxxx == trigger.oldmap.get(l.id).zipxxx) continue;

 

Best, Steve.