0

I currently have a class that populates the related lists of all child records with a contact role based on the creation of a contact on the parent record. An issue I ran into is that whenever the contact role is edited, rather than replacing the current role on the child records with the new value it adds it again under the previous contact role. I tried creating a trigger that replaces the old value with the new one if if the contact name is the same but nothing is happening when fired. Can anyone point me in the right direction?

Trigger:

trigger LAM_DuplicateContactRole on Contact (before insert, before update) {

     map<Id, Contact> existingContactMap = new  map<Id, Contact>([Select Id, Name, Customer_Role__c From Contact Where Customer_Role__c != null]); 
     map<Id, AccountContactRole> existingACRoleMap = new map<Id, AccountContactRole>([Select Id, Role FROM AccountContactRole]);

     for(Contact c : Trigger.new){
         if(Trigger.isUpdate){
        if(c.name == existingContactMap.get(c.Id).Name && c.Customer_Role__c != existingACRoleMap.get(c.Id).Role){
                LAM_RelatedListUpdate.RollContactRolesToAccountChildren(trigger.new);
            }
           upsert c;

        }
     }

}

CLASS THAT HANDELS THE CONTACT ROLE ROLL DOWN ON ACCOUNT HIERARCHY:

public class LAM_RelatedListUpdate {
    Public static void RollContactRolesToAccountChildren(List<Contact> con){
        //variables for the account lam record types
         Id LAMCRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByName().get('LAM Customer Account').getRecordTypeId();//get recordtypeID of Large Customer
         Id LAMSRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByName().get('LAM Site Account').getRecordTypeId();//get recordtypeID of Large Customer
         Id LAMDRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByName().get('LAM Division Account').getRecordTypeId() ;//get recordtypeID of Large Customer


        //list of child accounts, parent accounts, contacts and contact roles
        List<Id> accountsFromContacts = new List<Id>();  
        List<Account> parentAccounts = new List<Account>();
        List<Account> listOfAccountChildren = new List<Account>();
        List<AccountContactRole> contactRolesClones = new List<AccountContactRole>();

        System.debug(con);

        for(contact c : con){
            accountsFromContacts.add(c.AccountId);
        }

        parentAccounts = [Select Id, RecordTypeId from Account Where Id = :accountsFromContacts ];


        listOfAccountChildren = [Select Id, RecordTypeId, Parent_Account_for_LAM__c from Account Where Parent_Account_for_LAM__c = :accountsFromContacts ];
        //updates the contact roles of the child accounts
        for(contact c : con){
            for(Account a : listOfAccountChildren ){
                if(isMatchingRecord(a.Parent_Account_for_LAM__c, parentAccounts) ){
                    AccountContactRole cloneConatactRole = new AccountContactRole( AccountId = a.id , ContactId = c.Id,  Role = c.Customer_Role__c  );
                    contactRolesClones.add(cloneConatactRole);
                }
            }
        }

        //Database.insert(contactRolesClones);
        //Database.update(contactRolesClones);

        Boolean isUpsertfirstTime = true;
        try{
            upsert contactRolesClones;
        }catch(DMLException e){
            if(isUpsertfirstTime){

                System.debug(e);
                isUpsertfirstTime = false;
            }
        }



    }
   /* public static void deleteChildRole(Id ParAcc, List<Contact> c){
      List<AccountContactRole> contactRolesClones = new List<AccountContactRole>();
      List<Contact> listOfAccountChildren = [Select Id, RecordTypeId, Parent_Account_for_LAM__c from Account Where Parent_Account_for_LAM__c = :accountsFromContacts ];




    }
*/
    //checks that the parent account is of these lam record types, and if the parentId matches the one of the child
    public static Boolean isMatchingRecord(Id ParAcc, List<Account> acc){
         Id LAMCRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByName().get('LAM Customer Account').getRecordTypeId();//get recordtypeID of Large Customer
         Id LAMSRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByName().get('LAM Site Account').getRecordTypeId();//get recordtypeID of Large Customer
         Id LAMDRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByName().get('LAM Division Account').getRecordTypeId() ;//get recordtypeID of Large Customer
        for (account a: acc){
            if (a.Id == ParAcc ){
                if(a.recordtypeid == LAMCRecordTypeId  ||  a.recordtypeid == LAMDRecordTypeId || a.recordtypeid == LAMSRecordTypeId){
                    return true;
                }
            }
        }

        return false;
    }



    public static void deleteChildAccountContactRoles(List<Contact> con){

        List<AccountContactRole> contactRoles = new List<AccountContactRole>();

        for (contact c : con){

            contactRoles = [Select id From AccountContactRole where contactId = :c.Id ]; 
        }

        delete contactRoles;


}
 public static void upsertnewContactRole(List<Contact> nCon){

        List<AccountContactRole> nContactRoles = new List<AccountContactRole>();         
        for (contact nc: nCon){
           // if(nc.FirstName == nCon.FirstName)
            nContactRoles = [Select id from AccountContactRole where contactId = :nc.Id];
        }
        upsert nContactRoles;
    }

}

 /***   for(Account a : accountsWithRoles ) {
            if(a.recordtypeid == LAMCRecordTypeId  ||  a.recordtypeid == LAMDRecordTypeId || a.recordtypeid == LAMSRecordTypeId) {


                for (Account acc :listOfAccountChildren ){
                    if(acc.ParentId == a.Id){
                        for(AccountContactRole ACR : a.AccountContactRoles){
                            AccountContactRole cloneConatactRole = new AccountContactRole( AccountId = acc.id , ContactId = ACR.ContactId,  Role = ACR.Role  );
                            contactRolesClones.add(cloneConatactRole);
                        }

                    }
                }

            } 
        } **/

        //AccountContactRole newConatactRole = new AccountContactRole( AccountId = acc.id , ContactId = ACR.ContactId,  Role = ACR.Role  );

TRIGGER FOR CLASS:

trigger LAM_Contact_ReleatedList on Contact (after insert, after update, after delete) {

    if(Trigger.isAfter){
        if(Trigger.IsInsert || Trigger.IsUpdate){
            LAM_RelatedListUpdate.RollContactRolesToAccountChildren(trigger.new);
        }
    }    
    if(Trigger.isAfter && Trigger.isDelete){
        LAM_RelatedListUpdate.deleteChildAccountContactRoles(trigger.old);
    }
}

2 Answers 2

0

I'm going to have to skip over the issues that I see in the code as they are numerous and the format of this site is not adequate for me to explain what they are, why they are bad and how to fix them.

Ultimately, I think that you are taking the wrong approach. By the very nature of the code and the recursiveness that would be required to achieve the goal in a trigger, you are very likely to hit Salesforce limits.

The approach I would advise, is to create a visualforce page that can be embedded into the account page. With this, instead of creating account contact role records, you could simply query for the account contact roles on each account in the hierarchy and display them in a list for your users. (Note, if you use lightning you can do the same with a lightning component).

The benefit here being that there is a limit of 100 account tiers which should be more then enough. This also keeps code out of your triggers meaning your triggers can be very simple and to the point. This prevents record bloat as well speeding up your org.

In this case, your account trigger only needs to create contact roles for it's own contacts and your contact trigger only needs to update the roles for it's own account. All other accounts in the hierarchy will pull in the roles just in time.

public class AccountService {
    public static void buildAccountContactRoles(Set<id> AccountIds) {
        List<Account> accountsToProcess = new List<Account>();

        for (Id i : accountIds) {
            accountsToProcess.add(new Account(Id = i));
        }

        buildAccountContactRoles(accountsToProcess);
    }

    public static void buildAccountContactRoles(List<Account> accounts) {
        accounts = getAccountsWithContactsAndRoles(accounts);

        // Delete existing contact roles
        // this saves effort of trying to update them
        List<AccountContactRole> acrsToDelete = new List<AccountContactRole>();
        List<AccountContactRole> acrsToInsert = new List<AccountContactRole>();

        for (Account a : accounts) {
            for (Contact c : a.contacts) {
                acrsToInsert.add(new AccountContactRole(
                    AccountId = a.Id,
                    ContactId = c.Id,
                    Role = c.Customer_Role__c
                ));
            }

            for (AccountContactRole acr : a.AccountContactRoles) {
                acrsToDelete.add(acr);
            }
        }

        delete acrsToDelete;
        insert acrsToInsert;
    }

    public static List<Account> getAccountsWithContactsAndRoles(List<Account> accounts) {
        Map<Id, Account> accountMap = new Map<Id, Account>(accounts);
        getAccountsWithContacts(accountMap);
    }

    public static List<Account> getAccountsWithContactsAndRoles(Map<Id, Account> accountMap() {
        getAccountsWithContacts(accountMap.keySet());
    }

    public static List<Account> getAccountsWithContactsAndRoles(Set<id> accountIds) {
        return [
            SELECT Id, (
                SELECT FirstName, LastName, Email, Customer_Role__c
                FROM Contacts
            ), (
                SELECT ContactId, Role
                FROM AccountContactRoles
            )
            FROM Account
            WHERE Id IN :accountIds
        ];
    }
}

Once you have that, you can write your trigger:

trigger AccountTrigger on Account (after update) {
    AccountService.buildAccountContactRoles(Trigger.new);
}

Finally, all you'll need to run the same code from the contact trigger.

trigger ContactTrigger on Contact(after insert, after update) {
    Set<Id> accountsToUpdate = Set<Id>();

    for (Contact c : trigger.new) {
        if (
            Trigger.isInsert ||
            c.Customer_Role__c != oldMap.get(c.Id).Customer_Role__c
        ) {
            accountsToUpdate.add(c.AccountId);
        }
    }

    if (!accountsToUpdate.isEmpty()) {
        AccountService.buildAccountContactRoles(accountsToUpdate);
    }
}

Now all you need to do is create your VF Component (due to time, I can't provide a code sample for now but I will when I get the time).

8
  • Thanks for your help but I think you're misunderstand the requirements I was given. I want the trigger on a Contact to update related lists on Accounts, not the list of the AccountContactRoles itself. Right now, upon creation of a Contact on a parent account the Name and Role of that Contact will appear on all child accounts of that parent. The issue that was pointed out to me is that when the Contact on the parent is edited the roles on the child records do not update it is simply added again.
    – user64312
    Commented Mar 13, 2019 at 16:02
  • Sure, I can definitely help with that as well, your question says you have a class that is performing that work for you. So chances are the behavior your expressing is a problem in that custom class. Can you provide the code of that class so we can take a look? But please also, review my answer as it does refer to several of the issues you have in your trigger that you should avoid.
    – gNerb
    Commented Mar 13, 2019 at 16:21
  • Its also worth noting that providing code will help prevent confusion. Your code will usually tell us what you're trying to do, much better then words will.
    – gNerb
    Commented Mar 13, 2019 at 16:42
  • Noted, I've updated my post with my class and trigger for the contact role logic I made
    – user64312
    Commented Mar 13, 2019 at 17:02
  • @BWilliad I updated my answer, the code I previously provided is interestingly still accurate. I'll try to provide a sample VF page for display purposes when I have more time.
    – gNerb
    Commented Mar 13, 2019 at 17:26
-1

One important thing is that your trigger is working only in before update case, so you do not need to write upsert statement in code. It will automatically update contact without firing DML.

1
  • 1
    This is good advice but there are so many more problems with OPs code and your answer doesn't actually address the issue.
    – gNerb
    Commented Mar 11, 2019 at 19:08

You must log in to answer this question.