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
John Neilan 18John Neilan 18 

Field Error Message in Trigger

Hello,

I have a trigger that is checking a picklist field on a custom object (VIP_Type__c) to make sure duplicate records are not allowed under the parent object (Contact).  I am getting an Apex error message related to the error message line in my trigger. Can anyone help me understand why?
 
trigger UpdateAcctVIP on VIP_Type__c (after insert, after update, after delete){

	Set<Id> acctIds = new Set<ID>();
	Set<Id> contIds = new Set<ID>();

	map<Id, set<String>> contact_type_map = new map<Id, set<String>>();

// Get all the Account & Contact Ids in the Set 
	if (Trigger.isDelete) {
		for(VIP_Type__c vip : Trigger.old){
			acctIds.add(vip.Account__c);
            contIds.add(vip.Contact__c);

			contact_type_map.put(vip.Contact__c , new set<String>());
		}
	}
	else{
        for(VIP_Type__c vip : Trigger.new){
			acctIds.add(vip.Account__c);
            contIds.add(vip.Contact__c);

            contact_type_map.put(vip.Contact__c, new set<String>{vip.VIP_Type__c});
System.Debug('@@@###-contact_Type_Map: '+contact_Type_Map);
	}
    }
		List<VIP_Type__c> vipRecs = [SELECT Id,Account__c,Contact__c,VIP_Type__c
                                    FROM VIP_Type__c
                                    WHERE Contact__c in:contIds];


//Check for VIP Types already entered for Contact
    for(VIP_Type__c objVIP: vipRecs){
		if(contact_type_map.get(objVIP.Contact__c).contains(objVIP.VIP_Type__c)){
system.debug('@@@### - Error message:  '+objVIP.VIP_Type__c);
                trigger.newMap.get(objVIP.Id).addError('This Type already exists for this Contact');
system.debug('@@@### - Error message:  '+trigger.newMap.get(objVIP.Id));
        }
        else{
            contact_type_map.get(objVIP.Contact__c).add(objVIP.VIP_Type__c);
        }
    }

 
Best Answer chosen by John Neilan 18
Abhishek BansalAbhishek Bansal
Hi John,

I have corrected all those errors as you mentioned, can you please use the updated code below:
trigger UpdateAcctVIP on VIP_Type__c (after insert, after update, after delete){

	Set<Id> acctIds = new Set<ID>();
	Set<Id> contIds = new Set<ID>();
	map<Id, set<VIP_Type__c>> contact_type_map = new map<Id, set<VIP_Type__c>>();

// Get all the Account & Contact Ids in the Set 
	if (Trigger.isDelete) {
		for(VIP_Type__c vip : Trigger.old){
			acctIds.add(vip.Account__c);
            contIds.add(vip.Contact__c);
//            contact_type_map.put(vip.Contact__c, new set<VIP_Type__c>());
		}
	}
	else{
        for(VIP_Type__c vip : Trigger.new){
			acctIds.add(vip.Account__c);
            contIds.add(vip.Contact__c);
//            contact_type_map.put(vip.Contact__c, new set<VIP_Type__c>{vip});
	}
    }
 List<VIP_Type__c> vipRecs = [SELECT Id,Account__c,Contact__c,VIP_Type__c
                                    FROM VIP_Type__c
                                    WHERE Contact__c in:contIds];

    Map<Id,set<string>> existingTypeMap = new Map<Id,set<string>>();

    for (VIP_Type__c type: vipRecs)
    {
        set<string> existingTypes = new set<string>();
        if(existingTypeMap.containsKey(type.contact__c))
        {
            existingTypes = existingTypeMap.get(type.contact__c);
        }
        existingTypes.add(type.VIP_Type__c);
        existingTypeMap.put(type.contact__c,existingTypes);
    }



    for (VIP_Type__c type : trigger.new)
    {
        if (existingTypeMap.containsKey(type.contact__c))
        {
            if (existingTypeMap.get(type.contact__c).contains(type.VIP_Type__c))
            {
                trigger.newMap.get(type.Id).addError('This Type already exists for this Contact');
            }
            else
            {
                Set<string> existingTypes = existingTypeMap.get(type.contact__c);
                existingTypes.add(type.VIP_Type__c);
                existingTypeMap.put(type.contact__c,existingTypes);
            }

        }
    }
    
// Query the Accounts
		List<Account> acct = new List<Account>();

// Use the VIP Types to get all the related Types for the Account 
			acct = [SELECT Id, VIP_Types__c,(Select VIP_Type__c FROM VIP_Types__r) 
					FROM Account 
					WHERE Id in :acctIds]; 

// Iterate over each Account and VIP record 
			for(Account a : acct){ 
				a.VIP_Types__c = ''; 

				for(VIP_Type__c vip: a.VIP_Types__r){ 

					if(!a.VIP_Types__c.contains(vip.VIP_Type__c) || a.VIP_Types__c == ''){ // Check if the Type is already in the Account Field. if not add it otherwise skip 

					a.VIP_Types__c += vip.VIP_Type__c + '; '; 
					} 
				} 
			} 
// Update the Account 
		update acct; 
}

Let me know if there is an issue.

Thanks,
Abhishek Bansal.

All Answers

Abhishek BansalAbhishek Bansal
Hi John,

You are using after insert in your trigger which means that the trigger code will run after the records are inserted in the database. So when you do a query at line no. 26 it will fetch the current records as well. 
As a resuly you will always see the error message when trying to insert any records. This can be solved by either changing the trigger context to before insert or adding one more check in the query i.e. AND ID NOT IN: trigger.new -> this will not include the new records.

Also when adding values to the set you are always creating a new set which should also be changed like this:
//Replace line no. 22 with below:
if(!contact_type_map.containsKey(vip.Contact__c)) {
				contact_type_map.put(vip.Contact__c, new set<String>());
			}
            contact_type_map.get(vip.Contact__c).add(vip.VIP_Type__c);

Let me know if you need any more information on this.

Thanks,
Abhishek Bansal.
John Neilan 18John Neilan 18
Thanks Abhishek!  Even with those changes, I am still getting an error message on line 35 when it should be throwing the duplicate error message:

System.NullPointerException: Attempt to de-reference a null object

Any idea why that might be happening?
Abhishek BansalAbhishek Bansal

Hi John,

As suggested by David, now the vipRecs list will not include the record so the map will always return null values. You need to create a new map to get this record based on the VIP_Type__c. Code will be like this:
Create one variable like  mapOfTypeWIthVIPType Map<String, VIP_Type__c>
When you are adding records in the contact_type_map Map, add the value in this map also like this -> mapOfTypeWIthVIPType.put(vip.VIP_Type__c, vip);

Now replace the line where you are getting error like this:
trigger.newMap.get(mapOfTypeWIthVIPType.get(objVIP.VIP_Type__c)).addError(''......);

If you are having any trouble in understanding this, please post your latest trigger so that i can make the changes.

Thanks,
Abhishek Bansal.

John Neilan 18John Neilan 18
Thanks David!  I'm getting 2 errors:

on Line 33 - Method does not exist or incorrect signature: void contains(String) from the type Set<VIP_Type__c>
on line 39 - Method does not exist or incorrect signature: void add(String) from the type Set<VIP_Type__c>

Is that because of the suggested changes?
David Zhu 🔥David Zhu 🔥
I have redone the code for you. There might be typo in the code. please fix by yourself. I removed AFTER DELETE since I don't see any need for that part.
 
trigger UpdateAcctVIP on VIP_Type__c (after insert, after update){

	Set<Id> acctIds = new Set<ID>();
	Set<Id> contIds = new Set<ID>();


    for(VIP_Type__c vip : Trigger.new){
        acctIds.add(vip.Account__c);
        contIds.add(vip.Contact__c);
    }
   
    List<VIP_Type__c> vipRecs = [SELECT Id,Account__c,Contact__c,VIP_Type__c
                                    FROM VIP_Type__c
                                    WHERE Contact__c in:contIds];


    Map<Id,set<string>> existingTypeMap = new Map<Id,set<string>>();

    for (VIP_Type__c type: vipRecs)
    {
        map<string> existingTypes = new set<string>();
        if(existingTypMap.contains(type.contact__c))
        {
            existTypes = existingTypeMap.get(type.contact__c);
        }
        existingTypes.add(type.VIP_Type__c);
        existingTypeMap.put(type.contact__c,existingTypes);
    }



    for (VIP_Type__c type : trigger.new)
    {
        if (existingTypeMap.contains(type.contact__c))
        {
            if (existingTypeMap.get(type.contact__c).contains(type.VIP_Type__c))
            {
                trigger.newMap.get(type.Id).addError('This Type already exists for this Contact');
            }
            else
            {
                map<string> existingTypes = existingTypeMap.get(type.contact__c);
                existingTypes.add(type.VIP_Type__c);
                existingTypeMap.put(type.contact__c,existingTypes);
            }

        }
    }


}

 
John Neilan 18John Neilan 18

Thanks David! I do need the after Delete because there is another piece of the trigger that adds & removes values to the associated Account. I was only trying to address the finding duplicate values with this post as the rest of the trigger works as expected.

I replaced my code with yours but I got quite a few errors. I wasn't quite following the new code, so I'm not sure if these are typo style errors or something bigger.  Thanks for your help!

Line 30 - Invalid type argument count for Map: expected 2 but found 1
Line 31 - Method does not exist or incorrect signature: void contains(Id) from the type Map<Id,Set<String>>
Line 33 - Illegal assignment from Set<String> to Map<String>
Line 35 - Variable does not exist: existingTypes (which is odd because that is the name of the Map)
Line 36 - Method does not exist or incorrect signature: void put(Id, Map<String>) from the type Map<Id,Set<String>>
Line 43 - Method does not exist or incorrect signature: void contains(Id) from the type Map<Id,Set<String>>
Line 51 - Invalid type argument count for Map: expected 2 but found 1
Line 52 - Variable does not exist: existingTypes
Line 53 - Method does not exist or incorrect signature: void put(Id, Map<String>) from the type Map<Id,Set<String>>


 

trigger UpdateAcctVIP on VIP_Type__c (after insert, after update, after delete){

	Set<Id> acctIds = new Set<ID>();
	Set<Id> contIds = new Set<ID>();
	map<Id, set<VIP_Type__c>> contact_type_map = new map<Id, set<VIP_Type__c>>();

// Get all the Account & Contact Ids in the Set 
	if (Trigger.isDelete) {
		for(VIP_Type__c vip : Trigger.old){
			acctIds.add(vip.Account__c);
            contIds.add(vip.Contact__c);
//            contact_type_map.put(vip.Contact__c, new set<VIP_Type__c>());
		}
	}
	else{
        for(VIP_Type__c vip : Trigger.new){
			acctIds.add(vip.Account__c);
            contIds.add(vip.Contact__c);
//            contact_type_map.put(vip.Contact__c, new set<VIP_Type__c>{vip});
	}
    }
 List<VIP_Type__c> vipRecs = [SELECT Id,Account__c,Contact__c,VIP_Type__c
                                    FROM VIP_Type__c
                                    WHERE Contact__c in:contIds];

    Map<Id,set<string>> existingTypeMap = new Map<Id,set<string>>();

    for (VIP_Type__c type: vipRecs)
    {
        map<string> existingTypes = new set<string>();
        if(existingTypeMap.contains(type.contact__c))
        {
            existingTypes = existingTypeMap.get(type.contact__c);
        }
        existingTypes.add(type.VIP_Type__c);
        existingTypeMap.put(type.contact__c,existingTypes);
    }



    for (VIP_Type__c type : trigger.new)
    {
        if (existingTypeMap.contains(type.contact__c))
        {
            if (existingTypeMap.get(type.contact__c).contains(type.VIP_Type__c))
            {
                trigger.newMap.get(type.Id).addError('This Type already exists for this Contact');
            }
            else
            {
                map<string> existingTypes = existingTypeMap.get(type.contact__c);
                existingTypes.add(type.VIP_Type__c);
                existingTypeMap.put(type.contact__c,existingTypes);
            }

        }
    }
    
// Query the Accounts
		List<Account> acct = new List<Account>();

// Use the VIP Types to get all the related Types for the Account 
			acct = [SELECT Id, VIP_Types__c,(Select VIP_Type__c FROM VIP_Types__r) 
					FROM Account 
					WHERE Id in :acctIds]; 

// Iterate over each Account and VIP record 
			for(Account a : acct){ 
				a.VIP_Types__c = ''; 

				for(VIP_Type__c vip: a.VIP_Types__r){ 

					if(!a.VIP_Types__c.contains(vip.VIP_Type__c) || a.VIP_Types__c == ''){ // Check if the Type is already in the Account Field. if not add it otherwise skip 

					a.VIP_Types__c += vip.VIP_Type__c + '; '; 
					} 
				} 
			} 
// Update the Account 
		update acct; 
}
Abhishek BansalAbhishek Bansal
Hi John,

I have corrected all those errors as you mentioned, can you please use the updated code below:
trigger UpdateAcctVIP on VIP_Type__c (after insert, after update, after delete){

	Set<Id> acctIds = new Set<ID>();
	Set<Id> contIds = new Set<ID>();
	map<Id, set<VIP_Type__c>> contact_type_map = new map<Id, set<VIP_Type__c>>();

// Get all the Account & Contact Ids in the Set 
	if (Trigger.isDelete) {
		for(VIP_Type__c vip : Trigger.old){
			acctIds.add(vip.Account__c);
            contIds.add(vip.Contact__c);
//            contact_type_map.put(vip.Contact__c, new set<VIP_Type__c>());
		}
	}
	else{
        for(VIP_Type__c vip : Trigger.new){
			acctIds.add(vip.Account__c);
            contIds.add(vip.Contact__c);
//            contact_type_map.put(vip.Contact__c, new set<VIP_Type__c>{vip});
	}
    }
 List<VIP_Type__c> vipRecs = [SELECT Id,Account__c,Contact__c,VIP_Type__c
                                    FROM VIP_Type__c
                                    WHERE Contact__c in:contIds];

    Map<Id,set<string>> existingTypeMap = new Map<Id,set<string>>();

    for (VIP_Type__c type: vipRecs)
    {
        set<string> existingTypes = new set<string>();
        if(existingTypeMap.containsKey(type.contact__c))
        {
            existingTypes = existingTypeMap.get(type.contact__c);
        }
        existingTypes.add(type.VIP_Type__c);
        existingTypeMap.put(type.contact__c,existingTypes);
    }



    for (VIP_Type__c type : trigger.new)
    {
        if (existingTypeMap.containsKey(type.contact__c))
        {
            if (existingTypeMap.get(type.contact__c).contains(type.VIP_Type__c))
            {
                trigger.newMap.get(type.Id).addError('This Type already exists for this Contact');
            }
            else
            {
                Set<string> existingTypes = existingTypeMap.get(type.contact__c);
                existingTypes.add(type.VIP_Type__c);
                existingTypeMap.put(type.contact__c,existingTypes);
            }

        }
    }
    
// Query the Accounts
		List<Account> acct = new List<Account>();

// Use the VIP Types to get all the related Types for the Account 
			acct = [SELECT Id, VIP_Types__c,(Select VIP_Type__c FROM VIP_Types__r) 
					FROM Account 
					WHERE Id in :acctIds]; 

// Iterate over each Account and VIP record 
			for(Account a : acct){ 
				a.VIP_Types__c = ''; 

				for(VIP_Type__c vip: a.VIP_Types__r){ 

					if(!a.VIP_Types__c.contains(vip.VIP_Type__c) || a.VIP_Types__c == ''){ // Check if the Type is already in the Account Field. if not add it otherwise skip 

					a.VIP_Types__c += vip.VIP_Type__c + '; '; 
					} 
				} 
			} 
// Update the Account 
		update acct; 
}

Let me know if there is an issue.

Thanks,
Abhishek Bansal.
This was selected as the best answer
John Neilan 18John Neilan 18
Thanks Abhishek!  It was thowing the error message for anything entered, but I added :
AND NOT IN:trigger.new

to the List and it solved the issue.  Thanks so much for your help!!
Abhishek BansalAbhishek Bansal
Yes that check was required as we are using after insert. Please choose a Best Answer and mark this question as Solved so that it can help others.