+ Start a Discussion
dlCamelotdlCamelot 

Easy Trigger (but not for me)

Trying to write a trigger that fires when a contact is created/updated.  The trigger would update a lookup field on the account (Account.Primary_Contact__c).  The correct ID comes from a contact record that has Contact.Primary_Contact__c = true.  

Why won't this work?

trigger fillPrimaryonAcct on Contact (before insert, before update) {
    //Collect IDs to update
    List<Id> cIdList = new list<Id>();
    List<Id> aIdList = new list<Id>();

    //Loop
    for(contact c: trigger.new) {
        aIdList.add(c.Id);
    
    }
    //List that grabs the Id of the primary contact
    List<Contact> pContact = [SELECT Id FROM Contact WHERE Primary_Contact__c = true, Id = :id];

}

Any better ways to do this would be appreciated!
Best Answer chosen by dlCamelot
JayantJayant
Check this out - 

trigger fillPrimaryonAcct on Contact (after insert, after update) {

    //map of Account Id vs the primary Contact record
    Map<Id, Contact> accountVsContacts = new Map<Id, Contact>();
    //list to hold Accounts that would be affected
    List<Account> accounts = new List<Account>();
    
    //collect all Contacts that are primary along with their parent Account's Id
    for(Contact c : Trigger.New)
    {
        if(c.Primary_Contact__c)
        {
            accountVsContacts.put(c.AccountId, c);
        }
    }
    
    //query all Accounts for which a primary contact exists in the batch
    accounts = [SELECT Id, Primary_Contact__c FROM Account WHERE Id IN :accountVsContacts.keySet()];
    
    //iterate over all Account records and set the primary Contact
    for(Account a : accounts)
    {
        a.Primary_Contact__c = accountVsContacts.get(a.Id).Id;
    }
    
    //commit changes to database
    update accounts;
}

All Answers

JayantJayant
1. Trigger.New does not have Ids during Before Insert, all would be null. You should use an After Insert instead.
2. In SOQL, filters need to be separated by an AND or OR. Comma won't work.
JayantJayant
Check this out - 

trigger fillPrimaryonAcct on Contact (after insert, after update) {

    //map of Account Id vs the primary Contact record
    Map<Id, Contact> accountVsContacts = new Map<Id, Contact>();
    //list to hold Accounts that would be affected
    List<Account> accounts = new List<Account>();
    
    //collect all Contacts that are primary along with their parent Account's Id
    for(Contact c : Trigger.New)
    {
        if(c.Primary_Contact__c)
        {
            accountVsContacts.put(c.AccountId, c);
        }
    }
    
    //query all Accounts for which a primary contact exists in the batch
    accounts = [SELECT Id, Primary_Contact__c FROM Account WHERE Id IN :accountVsContacts.keySet()];
    
    //iterate over all Account records and set the primary Contact
    for(Account a : accounts)
    {
        a.Primary_Contact__c = accountVsContacts.get(a.Id).Id;
    }
    
    //commit changes to database
    update accounts;
}
This was selected as the best answer
JayantJayant
If this is resolved, please do mark the question as Resolved and the most appropriate/helpful answer as the best answer :).
If none of the answers helped you significantly, please post the solution. You may also mark your solution as the best answer.
dlCamelotdlCamelot
This is much closer.  It works for new and updating of primary contacts.  However, if a contact is no longer marked primary, the FOR loop does not pick it up.  Is there any way to make sure, if no primary contact is listed, then the first contact created is made 'primary'?
 
JayantJayant
Yes, there is.

Above code was just to give you an idea about how to approach your Usecase.
May be you should give it a try first and then if you are stuck, you can always get help. Some hints to make your task easier - 

1. It would simplify the solution, if you can utilize a relationship query like - 
[SELECT Id, Primary_Contact__c, (Select Id, Primary_Contact__c FROM Contacts) FROM Account WHERE Id IN :accountVsContacts.keySet()]. Also, you can have any valid clause/filter inside the inner query.
2. You may want some of your code to just execute during Update and not during Insert. Can be done using Trigger context variables like isInsert and isUpdate.
3. You may want to figure out if a Contact was earlier set as Primary and has been removed (as Primary) during the Update or vice-versa. Best way is to use something like - 
for(Contact c : Trigger.New)
{
  if(c.Primary_Contact__c != Trigger.oldMap.get(c.Id).Primary_Contact__c)
  {
    //your code to handle this
  }
}

Happy Coding !!!