0

I have a custom object called Account Relation. When creating a record on this object which is related to an Account. I need to automatically create a 2nd record on another related Account record with inverted values

When creating the first record I have this values enter image description here

and I would like my trigger to create another AccountRelation record after the creation of the first record with the inverted values of the related record. enter image description here

I believe that the trigger should be a before trigger on the custom object. But my trigger never enter the second for loop here is the handler

public class AccountRelationHandler {

public static void createAccountRelation(List<Account_Relation__c> accRelationList) {
    
    List<Account_Relation__c> arList = new List<Account_Relation__c>();
    Map<Id,Id> mapofParAccIdVsRelAccId = new Map<Id,Id>();        
    
    for(Account_Relation__c accRel : accRelationList) {
        if(accRel.Account__c != null && accRel.Related_Account__c != null) {
            mapofParAccIdVsRelAccId.put(accRel.Account__c,accRel.Related_Account__c);
        }
    }
    System.debug('mapofParAccIdVsRelAccId ' +mapofParAccIdVsRelAccId);
    
    List<Account> accList = [SELECT Name FROM Account WHERE Id NOT IN(SELECT Account__c FROM Account_Relation__c)];
    
    System.debug('Account with no Relation ' +accList);
    
    for(Account_Relation__c ar : [SELECT Name, Account__c, Related_Account__c 
                                  FROM Account_Relation__c WHERE Related_Account__c IN :mapofParAccIdVsRelAccId.keySet()]) {
                                    
    System.debug('Iteration Map with Related account ' +mapofParAccIdVsRelAccId);
        if(accList.size() > 0 && ar.Related_Account__c == accList[0].Id) {
            for(Account a : accList) {
                Account_Relation__c arl = new Account_Relation__c(
                    Name = a.Name,
                    Account__c = ar.Related_Account__c,
                    Related_Account__c = ar.Account__c );
                
                System.debug('New Account_Relation__c arl ' +arl.Id+ ' ' +arl.Name); 
                
                arList.add(arl);
                System.debug('List arList with new related record ' +arList);
            }        
                                          
       }
            
    }
    insert arList;
    
}

}
0

First, your trigger must definitely be after insert, or you will be unable to "see" the new incoming records, and you'll likely run in to an infinite loop.

Second, you cannot query may be effectively the entire database on line 15. You need to be more selective than that.

It looks like you're trying to create the inverse relationship when a record is created, without running into infinite recursion. This is one of those times when it's probably worth investing in "recursion blocking." This would simplify your code to just:

public class AccountRelationHandler {
    static Boolean isRecursion = false;
    public static void createAccountRelation(List<Account_Relation__c> accRelationList) {
        // We are creating the inverse records, so return now
        if(isRecursion) {
            return;
        }
        isRecursion = true;
        Account_Relation__c[] newRelations = new Account_Relation__c[0];
        for(Account_Relation__c a : accRelationList) {
            newRelations.add(new Account_Relation__c(Name=a.Name, Account__c=a.Related_Account__c, Related_Account__c=a.Account__c));
        }
        insert newRelations;
        isRecursion = false;
    }
}

Note that we reset the recursion flag at the end of the trigger to avoid blocking further invocations that may happen as a result of a unit test, Apex Code, etc. This is an import part of the formula.

However, do note that this code automatically assumes the inverse relationship does not exist in the database. It will create duplicate inverse relationships if you use this code and repeatedly insert records (you may want to write additional code later to block duplicates).

To fix your current idea, you need to bulkify your code to find potentially many matches. Here's what that looks like:

public class AccountRelationHandler {
    public static void createAccountRelation(List<Account_Relation__c> accRelationList) {
        Map<Account_Relation__c, Account_Relation__c> newRelations = new Map<Account_Relation__c, Account_Relation__c>();
        Set<Id> accountIds = new Set<Id>();
        Set<Id> relatedAccountIds = new set<Id>();
        Set<Name> relationNames = new Set<Name>();
        for(Account_Relation__c relation: accRelationList) {
            // Nothing to do, we don't match if a field is missing
            if(relation.Account__c == null || relation.Related_Account__c == null) {
                continue;
            }
            // Inverted because these are the matches we want to insert if they do not exist.
            accountIds.add(relation.Related_Account__c);
            relatedAccountIds.add(relation.Account__c);
            relationNames.add(relation.Name);
            // The data we want to insert if no matches found
            // Because comparison with sObject key is case sensitive, we use uppercase letters to match case-insensitive
            newRelations.put(
                new Account_Relation__c(
                    Name=relation.Name.toUpperCase(),
                    Related_Account__c=relation.Account__c,
                    Account__c=relation.Related_Account__c
                ),
                new Account_Relation__c(
                    Name=relation.Name,
                    Related_Account__c=relation.Account__c,
                    Account__c=relation.Related_Account__c
                )
            );
        }
        // We have no work to do, exit early
        if(newRelations.isEmpty()) {
            return;
        }
        // Find existing relationships
        for(Account_Relation__c relation: [
                SELECT Name, Account__c, Related_Account__c 
                FROM Account_Relation__c 
                WHERE Name = :relationNames AND Account__c = :accountIds AND Related_Account__c = :relatedAccountIds]) {
            // And remove them from the list to insert
            newRelations.remove(new Account_Relation__c(Name=relation.Name.toUpperCase(), Account__c=relation.Account__c, Related_Account__c=relation.Related_Account__c));
        }
        // We need a list to insert, so convert to a list
        Account_Relation__c[] recordsToInsert = newRelations.values();
        // Insert on empty list does not waste resources, so we don't need to check if statements
        insert recordsToInsert;
    }
}

You could also use a similar design to create code to block duplicates using the above pattern. This would run before insert to avoid calling the above logic after insert. You would use sObject.addError to prevent those records from being created.

Alternatively, you could use the data you gathered to block duplicates in the after insert trigger, if appropriate. I leave that as an exercise to the reader..

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.