You need to sign in to do that
Don't have an account?
Deek
Contact Trigger Help
Hi All,
We have a custom check box field in contact as Potential Contact - "Pot_Contact__c".
The requirement is when we add or update a contact for an account and if there is an existing contact with the above check box field checked, it should throw error or otherwise continue saving the contact record.
Also there should be atleast one contact with the check box checked when creating a new or existing contact.
Hope I am clear in my statement.
The trigger written so far is as below but for some reason its not working as expected. When trying to create a new contact or updating a existing contact I am getting both the error messages and not allowing me to save the contact.
I am sure I have made a major mistake but cant rectify due to lack of coding expereince. Please help to fix.
trigger PotentialContactCheckbox on Contact (before insert,before update) {
set<id> conaccids=new set<id>();
map<id,List<contact>> relcontactsmap=new map<id,List<contact>>();
List<contact> cons=new List<contact>();
List<Account> accts=new List<Account>();
for(contact con:trigger.new){
conaccids.add(con.AccountId);
}
accts=[select id,name from Account where id=:conaccids];
for(Account acc:accts){
id accid=acc.id;
if (Trigger.isUpdate)
{
cons=[select id,Pot_Contact__c from contact where AccountId=:accid AND id not in :trigger.old];
}
else
{
cons=[select id,Pot_Contact__c from contact where AccountId=:accid];
}
if(cons.size()>0){
relcontactsmap.put(accid,cons);
}
}
for(contact con1:trigger.new){
if((Trigger.isInsert) || (Trigger.isUpdate) ) {
boolean checkCC=false;
if(con1.Pot_Contact__c==true){
if(relcontactsmap.containsKey(con1.AccountId)){
con1.Pot_Contact__c.adderror('There is already a Potential Contact associated to this Account. Please uncheck and try saving the Contact.');
}
checkCC=true;
break;
}
if(con1.Pot_Contact__c==false){
if(relcontactsmap.containsKey(con1.AccountId)){
con1.Pot_Contact__c.adderror('There has to be one Potential Contact associated to this Account. Please check the check box and try saving the Contact.');
}
}
}
}
We have a custom check box field in contact as Potential Contact - "Pot_Contact__c".
The requirement is when we add or update a contact for an account and if there is an existing contact with the above check box field checked, it should throw error or otherwise continue saving the contact record.
Also there should be atleast one contact with the check box checked when creating a new or existing contact.
Hope I am clear in my statement.
The trigger written so far is as below but for some reason its not working as expected. When trying to create a new contact or updating a existing contact I am getting both the error messages and not allowing me to save the contact.
I am sure I have made a major mistake but cant rectify due to lack of coding expereince. Please help to fix.
trigger PotentialContactCheckbox on Contact (before insert,before update) {
set<id> conaccids=new set<id>();
map<id,List<contact>> relcontactsmap=new map<id,List<contact>>();
List<contact> cons=new List<contact>();
List<Account> accts=new List<Account>();
for(contact con:trigger.new){
conaccids.add(con.AccountId);
}
accts=[select id,name from Account where id=:conaccids];
for(Account acc:accts){
id accid=acc.id;
if (Trigger.isUpdate)
{
cons=[select id,Pot_Contact__c from contact where AccountId=:accid AND id not in :trigger.old];
}
else
{
cons=[select id,Pot_Contact__c from contact where AccountId=:accid];
}
if(cons.size()>0){
relcontactsmap.put(accid,cons);
}
}
for(contact con1:trigger.new){
if((Trigger.isInsert) || (Trigger.isUpdate) ) {
boolean checkCC=false;
if(con1.Pot_Contact__c==true){
if(relcontactsmap.containsKey(con1.AccountId)){
con1.Pot_Contact__c.adderror('There is already a Potential Contact associated to this Account. Please uncheck and try saving the Contact.');
}
checkCC=true;
break;
}
if(con1.Pot_Contact__c==false){
if(relcontactsmap.containsKey(con1.AccountId)){
con1.Pot_Contact__c.adderror('There has to be one Potential Contact associated to this Account. Please check the check box and try saving the Contact.');
}
}
}
}
https://developer.salesforce.com/forums/?id=906F00000009n7SIAQ
please see if this helps
I know but that didnt fix the problem completely. Also there is a new addition where I have mentioned that there needs to be atleast one potential contact when adding or updating contacts.
Could you please advise further Ramu?
Please elaborate with an example on your second requirement so that I can modify the code that Chidambar has suggested you earlier.
I have done refactoring of your code due to bad practice of using SOQL inside the loop.
The code for your task:
Most of the code requirements worked like a charm. The only thing thats not working is as below:
Say I have 3 contacts for an account with one of them as a Potential contact checbox checked. When i edit and uncheck the potential contact check box, it allows me to save the contact. Thats means now I have 3 contacts with the potential contact check box Unchecked.
Could you please help me with this so that at any point of time there should be a Potential contact for an Account.
Thanks for all the help in advance.
Yes, you are completely right. Change the line 27 to the following code:
That's perfectly worked. A BIG Kudos to you.
Last but not the least. For making it scalable in future, if we want to allow more than 1 potential contact how do we achieve that.
And finally code in the loop:
You are a real genius in apex coding. Really appreciate your quick help.
Sorry, my bad, which I forgot to mention during testing yesterday.
Delete Operation:
When we delete a contact with Pot_Contact__c as True, thats fine--- but it should make the existing contact as TRUE which is recently created.
Otherwise if there is just 1 contact as True and we are deleting, create a new Contact record with Pot_Contact__c as True for that account.
Could you please help on this?
Below is the final code you have given so far.
trigger PotentialContactCheckbox on Contact (before insert,before update) {
// get set of ID for related Accounts
Set<id> accIds = new Set<id>();
for (Contact con: Trigger.new) {
if ( con.AccountId != null ) {
accIds.add(con.AccountId);
}
}
// find contacts with Pot_Contact__c equals to TRUE only for related Accounts
List<Contact> relContacts = [SELECT AccountId FROM Contact WHERE Pot_Contact__c = true AND AccountId IN :accIds];
// build a map with key = AccountId and value = ContactId
Map<Id,Id> relAccContMap = new Map<Id,Id>();
for (Contact con: relContacts) {
relAccContMap.put(con.AccountId, con.Id);
}
// check for correct set of Pot_Contact__c field
for (Contact con: Trigger.new) {
Id relContactId = relAccContMap.get(con.AccountId); // ID of Contact or null
if ( con.Pot_Contact__c ) {
if ( relContactId != null && relContactId != con.Id ) {
con.Pot_Contact__c.addError('There is already a Potential Contact associated to this Account. Please uncheck and try saving the Contact.');
}
} else {
if ( (relContactId == null || relContactId == con.Id) && con.AccountId != null ) {
con.Pot_Contact__c.addError('There has to be one Potential Contact associated to this Account. Please check the check box and try saving the Contact.');
}
}
}
}
Your logic for delete operation isn't clear for me. I accept with part where we set Pot_Contact__c as True for recently created contact. But I don't understand why should we create new Contact after deleting last contact for Account?
I think the better way is to prevent of deleting of Contact with Pot_Contact__c equals to true if there is another Contacts related to the Account. And you should allow to delete last Contact without checking state of Pot_Contact__c field.
Thats indeed a very good suggestion. Thanks.
Delete Operation:
1.When we delete a contact with Pot_Contact__c as True, thats fine--- but it should make the existing contact as TRUE which is recently created.
2.if there is another Contacts related to the Account - allow to delete last Contact without checking state of Pot_Contact__c field.
Hope you concur as well. Where do we put in place this new piece of code.
Could you please send me the delete logic when you get time? I am really stuck up at this point.
Thanks in Advance.
Logic for delete operation we will put on after delete event of trigger.
Of course, deletion logic when multiple potential contacts are allowed will be slightly different.
Sorry to say but the delete logic is not firing at all.
I tested with 2 contacts for an Account with one of them as a Pot Contact. When I deleted the contact with the check marked its not making the other one as Pot ential contact. I believe we are on the same page with the delete logic.
Please help.
trigger PotentialContactCheckbox on Contact (before insert,before update) {
// get set of ID for related Accounts
Set<id> accIds = new Set<id>();
for (Contact con: Trigger.new) {
if ( con.AccountId != null ) {
accIds.add(con.AccountId);
}
}
// find contacts with Pot_Contact__c equals to TRUE only for related Accounts
List<Contact> relContacts = [SELECT AccountId FROM Contact WHERE Pot_Contact__c = true AND AccountId IN :accIds];
// build a map with key = AccountId and value = ContactId
Map<Id,Id> relAccContMap = new Map<Id,Id>();
for (Contact con: relContacts) { //WHERE Pot_Contact__c = true
relAccContMap.put(con.AccountId, con.Id);
}
// check for correct set of Pot_Contact__c field
for (Contact newcon: Trigger.new) {
Id relContactId = relAccContMap.get(newcon.AccountId); // ID of existing Contact or null
if((Trigger.isInsert) || (Trigger.isUpdate) ) {
if ( newcon.Pot_Contact__c ) {
if ( relContactId != null && relContactId != newcon.Id ) {
newcon.Pot_Contact__c.addError('There is already a Potential Contact associated to this Account. Please uncheck and try saving the Contact.');
}
} else {
if ( (relContactId == null || relContactId == newcon.Id) && newcon.AccountId != null ) {
newcon.Pot_Contact__c.addError('There has to be one Potential Contact associated to this Account. Please check the check box and try saving the Contact.');
}
}
}
}
// DELETE LOGIC
if ( Trigger.isAfter && Trigger.isDelete ) {
// get set of ID for related Accounts for OLD (deleted records) with Pot_Contact__c == true
Set<id> aIds = new Set<id>();
for (Contact con: Trigger.old) {
if ( con.AccountId != null && con.Pot_Contact__c ) {
aIds.add(con.AccountId);
}
}
// find contacts for related Accounts
List<Contact> lastContacts = [SELECT AccountId FROM Contact WHERE AccountId IN :aIds];
// build a map with key = AccountId and value = recently created Contact
Map<Id,Contact> accContMap = new Map<Id,Contact>();
for (Contact con: lastContacts) {
Contact c = accContMap.get(con.AccountId);
if ( c == null || con.CreatedDate > c.CreatedDate ) {
accContMap.put(con.AccountId, con);
}
}
// set Pot_Contact__c field equals to true for recently created Contacts
for (Contact con: accContMap.values()) {
con.Pot_Contact__c = true;
}
update accContMap.values();
}
}
And one more thing. It is better to put logic for insert and update into "if" block. It prevents potential errors and unnecessary SQOL queries.
As advised I have added the necessary code. Now the I am getting error message when I am deleteing contact with the check box true as well as for contact with check box as false.
Validation Errors While Saving Record(s)
There were custom validation error(s) encountered while saving the affected record(s). The first validation error encountered was "Apex trigger PotentialContactCheckbox caused an unexpected exception, contact your administrator: PotentialContactCheckbox: execution of AfterDelete caused by: System.NullPointerException: Attempt to de-reference a null object: Trigger.PotentialContactCheckbox: line 5, column 1".
Please advise.
Below is the last version of the code.
trigger PotentialContactCheckbox on Contact (before insert,before update, after delete) {
// get set of ID for related Accounts
Set<id> accIds = new Set<id>();
for (Contact con: Trigger.new) {
if ( con.AccountId != null ) {
accIds.add(con.AccountId);
}
}
// find contacts with Pot_Contact__c equals to TRUE only for related Accounts
List<Contact> relContacts = [SELECT AccountId FROM Contact WHERE Pot_Contact__c = true AND AccountId IN :accIds];
// build a map with key = AccountId and value = ContactId
Map<Id,Id> relAccContMap = new Map<Id,Id>();
for (Contact con: relContacts) { //WHERE Pot_Contact__c = true
relAccContMap.put(con.AccountId, con.Id);
}
// check for correct set of Pot_Contact__c field
for (Contact newcon: Trigger.new) {
Id relContactId = relAccContMap.get(newcon.AccountId); // ID of existing Contact or null
if ( Trigger.isBefore && (Trigger.isInsert || Trigger.isUpdate) ) {
if ( newcon.Pot_Contact__c ) {
if ( relContactId != null && relContactId != newcon.Id ) {
newcon.Pot_Contact__c.addError('There is already a Potential Contact associated to this Account. Please uncheck and try saving the Contact.');
}
} else {
if ( (relContactId == null || relContactId == newcon.Id) && newcon.AccountId != null ) {
newcon.Pot_Contact__c.addError('There has to be one Potential Contact associated to this Account. Please check the check box and try saving the Contact.');
}
}
}
}
// DELETE LOGIC
if ( Trigger.isAfter && Trigger.isDelete ) {
// get set of ID for related Accounts for OLD (deleted records) with Pot_Contact__c == true
Set<id> aIds = new Set<id>();
for (Contact con: Trigger.old) {
if ( con.AccountId != null && con.Pot_Contact__c ) {
aIds.add(con.AccountId);
}
}
// find contacts for related Accounts
List<Contact> lastContacts = [SELECT AccountId FROM Contact WHERE AccountId IN :aIds];
// build a map with key = AccountId and value = recently created Contact
Map<Id,Contact> accContMap = new Map<Id,Contact>();
for (Contact con: lastContacts) {
Contact c = accContMap.get(con.AccountId);
if ( c == null || con.CreatedDate > c.CreatedDate ) {
accContMap.put(con.AccountId, con);
}
}
// set Pot_Contact__c field equals to true for recently created Contacts
for (Contact con: accContMap.values()) {
con.Pot_Contact__c = true;
}
update accContMap.values();
}
}
You are too too good. Thanks once agin for all your help so far. The delete logic is now perfectly working fine.
In the insert update logic there is a help required. A logic change in the error message:"There is already a Potential Contact associated to this Account. Please uncheck and try saving the Contact."
--When user creates a new contact with check box ticked, the trigger should allow to save but make the check box Unchecked for the existing contact rather than throwing the above error message.
For my learning I was trying to modify the code but not successfull. Got the error message at line34:- relContactId.Campaign_Contact__c=false --:
Error: Compile Error: Initial term of field expression must be a concrete SObject: Id at line 34 column 17
Below is the overall code:
trigger PotentialContactCheckbox on Contact (before insert,before update, after delete) {
// START UPDATE and INSERT LOGIC
if ( Trigger.isBefore && (Trigger.isInsert || Trigger.isUpdate) ) {
// get set of ID for related Accounts
Set<id> accIds = new Set<id>();
for (Contact con: Trigger.new) {
if ( con.AccountId != null ) {
accIds.add(con.AccountId);
}
}
// find contacts with Pot_Contact__c equals to TRUE only for related Accounts
List<Contact> relContacts = [SELECT AccountId FROM Contact WHERE Pot_Contact__c = true AND AccountId IN :accIds];
// build a map with key = AccountId and value = ContactId
Map<Id,Id> relAccContMap = new Map<Id,Id>();
for (Contact con: relContacts) { //WHERE Pot_Contact__c = true
relAccContMap.put(con.AccountId, con.Id);
}
// check for correct set of Pot_Contact__c field
for (Contact newcon: Trigger.new) {
Id relContactId = relAccContMap.get(newcon.AccountId); // ID of existing Contact or null
if ( newcon.Pot_Contact__c ) {
if ( relContactId != null){ // && relContactId != newcon.Id ) {
// newcon.Pot_Contact__c.addError('There is already a Potential Contact associated to this Account. Please uncheck and try saving the Contact.');
relContactId.Pot_Contact__c=false;
}
} else {
if ( (relContactId == null || relContactId == newcon.Id) && newcon.AccountId != null ) {
newcon.Pot_Contact__c.addError('There has to be one Potential Contact associated to this Account. Please check the check box and try saving the Contact.');
}
}
// }
}
} // END UPDATE and INSERT LOGIC
// START DELETE LOGIC
if ( Trigger.isAfter && Trigger.isDelete ) {
// get set of ID for related Accounts for OLD (deleted records) with Pot_Contact__c == true
Set<id> aIds = new Set<id>();
for (Contact con: Trigger.old) {
if ( con.AccountId != null && con.Pot_Contact__c ) {
aIds.add(con.AccountId);
}
}
// find contacts for related Accounts
List<Contact> lastContacts = [SELECT AccountId,CreatedDate FROM Contact WHERE AccountId IN :aIds];
// build a map with key = AccountId and value = recently created Contact
Map<Id,Contact> accContMap = new Map<Id,Contact>();
for (Contact con: lastContacts) {
Contact c = accContMap.get(con.AccountId);
if ( c == null || con.CreatedDate > c.CreatedDate ) {
accContMap.put(con.AccountId, con);
}
}
// set Pot_Contact__c field equals to true for recently created Contacts
for (Contact con: accContMap.values()) {
con.Pot_Contact__c = true;
}
update accContMap.values();
} // END DELETE LOGIC
}
Before for loop we declare empty list of Contact. If there is another contact with Pot_Contact__c == true we will add it to list. After that list will be updated.
Without extra bracket ")" in the middle.
Thx for the quick response.
Below is the error message I am getting when I am adding a contact with the check box checked.
Error: Invalid Data.
Review all error messages below to correct your data.
Apex trigger PotentialContactCheckbox caused an unexpected exception, contact your administrator: PotentialContactCheckbox: execution of BeforeInsert caused by: System.DmlException: Update failed. First exception on row 0 with id 003M000000SpFgmIAF; first error: FIELD_CUSTOM_VALIDATION_EXCEPTION, There has to be one Potential Contact associated to this Account. Please check the check box and try saving the Contact.: [Campaign_Contact__c]: Trigger.PotentialContactCheckbox: line 40, column 1
below is the code so far:
trigger PotentialContactCheckbox on Contact (before insert,before update, after delete) {
// START UPDATE and INSERT LOGIC
if ( Trigger.isBefore && (Trigger.isInsert || Trigger.isUpdate) ) {
// get set of ID for related Accounts
Set<id> accIds = new Set<id>();
for (Contact con: Trigger.new) {
if ( con.AccountId != null ) {
accIds.add(con.AccountId);
}
}
// find contacts with Pot_Contact__c equals to TRUE only for related Accounts
List<Contact> relContacts = [SELECT AccountId FROM Contact WHERE Pot_Contact__c = true AND AccountId IN :accIds];
// build a map with key = AccountId and value = ContactId
Map<Id,Id> relAccContMap = new Map<Id,Id>();
for (Contact con: relContacts) { //WHERE Pot_Contact__c = true
relAccContMap.put(con.AccountId, con.Id);
}
// check for correct set of Pot_Contact__c field
List<Contact> clearList = new List<Contact>();
for (Contact newcon: Trigger.new) {
Id relContactId = relAccContMap.get(newcon.AccountId); // ID of existing Contact or null
if ( newcon.Pot_Contact__c) {
if ( relContactId != null && relContactId != newcon.Id ) {
clearList.add(new Contact(id=relContactId, Pot_Contact__c=false));
}
} else {
if ( (relContactId == null || relContactId == newcon.Id) && newcon.AccountId != null ) {
newcon.Pot_Contact__c.addError('There has to be one Potential Contact associated to this Account. Please check the check box and try saving the Contact.');
}
}
}
update clearList;
} // END UPDATE and INSERT LOGIC
// START DELETE LOGIC
if ( Trigger.isAfter && Trigger.isDelete ) {
// get set of ID for related Accounts for OLD (deleted records) with Pot_Contact__c == true
Set<id> aIds = new Set<id>();
for (Contact con: Trigger.old) {
if ( con.AccountId != null && con.Pot_Contact__c ) {
aIds.add(con.AccountId);
}
}
// find contacts for related Accounts
List<Contact> lastContacts = [SELECT AccountId,CreatedDate FROM Contact WHERE AccountId IN :aIds];
// build a map with key = AccountId and value = recently created Contact
Map<Id,Contact> accContMap = new Map<Id,Contact>();
for (Contact con: lastContacts) {
Contact c = accContMap.get(con.AccountId);
if ( c == null || con.CreatedDate > c.CreatedDate ) {
accContMap.put(con.AccountId, con);
}
}
// set Pot_Contact__c field equals to true for recently created Contacts
for (Contact con: accContMap.values()) {
con.Pot_Contact__c = true;
}
update accContMap.values();
} // END DELETE LOGIC
}
-
First when you create new Contact with Pot_Contact__c = true.
-
Second when your trigger try to update existing Contact and set Pot_Contact__c = false.
You can hotfix this situation with boolean static variable. There are a lot of examples how to do this.In brief, you have to declare boolean property somewhere in Apex class (not in trigger!):
And at the top of trigger put code of check:
Use name of your Apex class instead of NAME_OF_APEX_CLASS.
Sorry for the late response.
I have declared a class as below and added your given code in the trigger as advised.
Is it correct now please?
public class pc {
public static boolean firstRun = true;
}
trigger PotentialContactCheckbox on Contact (before insert,before update, after delete) {
if ( pc.firstRun ) {
pc.firstRun = false;
} else {
System.debug('Already ran!');
return;
}
// START UPDATE and INSERT LOGIC
if ( Trigger.isBefore && (Trigger.isInsert || Trigger.isUpdate) ) {
// get set of ID for related Accounts
Set<id> accIds = new Set<id>();
for (Contact con: Trigger.new) {
if ( con.AccountId != null ) {
accIds.add(con.AccountId);
}
}..................................