0

I would like to update the Household Naming Order field in NPSP. This field controls the order of contacts, which in turn informs the household salutation field (among others). My code successfully pulls the related Contact records to a household Account into a list in the order I want. (i.e. RecordType.Name, Primary Contact first, then descending Age and Gender.)

The next step is to then update each Contact record, and assign to the Household Naming Order field the same Integer as its list position.

However, I am getting a DML Exception error when I try to save a record.

I am brand new to coding, so I suspect there are more efficient ways to get my records in the correct order. My attempts at using a FOR LOOP failed (see commented out section), so I went back to using a FOR EACH LOOP -- but my gut tells me I should be able to do this with the later. I also know I shouldn't be updating a record in a LOOP, but I didn't see another way to do that either.

Anybody willing to help a beginner would be greatly appreciated! ~Steve

```

trigger HHNamingOrdering on Account (before update) {
        // Accounts will be updated with new Account Name as new Contacts are added or updated. So adding a new Contact will fire this Trigger.
        for (Account updatedAcct : Trigger.new) {
            
            //Create multiple SOQL queries to pull Contact records in prefered order
            //1st, get list of related 'Parent' Contacts in Household(i.e. Account)
            List<Contact> hhParents = [SELECT Id,
                                               RecordType.Name,
                                               Account.npe01__One2OneContact__c,
                                               Age__c,
                                               Gender__c,
                                               npo02__Household_Naming_Order__c
                                          FROM Contact
                                         WHERE RecordType.Name = 'Parent'
                                               AND Account.Id = :updatedAcct.Id
                                      ORDER BY npsp__Primary_Contact__c DESC, Age__c DESC, Gender__c DESC];
            System.debug(hhParents.size());
            
            //create a new empty list to store results: tempContacts
            List<Contact> tempContacts = new List<Contact>();
            
            //add the records found in the SOQL query to this empty list
            for (Contact hhParent : hhParents) {
                tempContacts.add(hhParent);
            }
            System.debug('List of tempContacts Parents ' + tempContacts.size());
    
            
            //2nd, get list of related 'Adult (Not a Parent)' Contacts
            List<Contact> hhNonParents = [SELECT Id,
                                               RecordType.Name,
                                               Account.npe01__One2OneContact__c,
                                               Age__c,
                                               Gender__c,
                                               npo02__Household_Naming_Order__c
                                          FROM Contact
                                         WHERE RecordType.Name = 'Adult (Not a Parent)'
                                               AND Account.Id = :updatedAcct.Id
                                      ORDER BY npsp__Primary_Contact__c DESC, Age__c DESC, Gender__c DESC];
            System.debug(hhNonParents.size());
            
            //add non-parent results to tempContacts list
            for (Contact hhNonParent : hhNonParents) {
                tempContacts.add(hhNonParent);
            }
            System.debug('List of tempContacts with nonParents ' + tempContacts.size());
    
            
           //3rd, get list of related 'Student' Contacts
            List<Contact> hhStudents = [SELECT Id,
                                               RecordType.Name,
                                               Account.npe01__One2OneContact__c,
                                               Age__c,
                                               Gender__c,
                                               npo02__Household_Naming_Order__c
                                          FROM Contact
                                         WHERE RecordType.Name = 'Student'
                                               AND Account.Id = :updatedAcct.Id
                                      ORDER BY npsp__Primary_Contact__c DESC, Age__c DESC, Gender__c DESC];
            
            //add Student results: tempContacts
            for (Contact hhStudent : hhStudents) {
                tempContacts.add(hhStudent);
            }
            System.debug('List of tempContacts with Students ' + tempContacts.size() );
            System.debug(tempContacts);
            
            
            //4th, get list of related Contacts who did meet the prior criteria
            List<Contact> hhOthers = [SELECT Id,
                                               RecordType.Name,
                                               Account.npe01__One2OneContact__c,
                                               Age__c,
                                               Gender__c,
                                               npo02__Household_Naming_Order__c
                                          FROM Contact
                                         WHERE RecordType.Name != 'Student'
                                               AND RecordType.Name != 'Adult (Not a Parent)'
                                               AND RecordType.Name != 'Adult'
                                               AND Account.Id = :updatedAcct.Id
                                      ORDER BY npsp__Primary_Contact__c DESC, Age__c DESC, Gender__c DESC];
            
            //add non-parent results: tempContacts
            for (Contact hhOther : hhOthers) {
                tempContacts.add(hhOther);
            }
            System.debug('List of tempContacts with Students ' + tempContacts.size() );
            System.debug(tempContacts);
            
            //the tempContacts list now includes all of the Contacts in the correct order
            //Now assign to each record in the list, the corresponding list position to the Household Naming Order field
            
            for (Contact tempContact : tempContacts) {
                Integer i = 0;
                tempContact.npo02__Household_Naming_Order__c = Integer.valueOf(tempContacts.get(i));
                update tempContact;
                i++;
            }
    /*        for (Integer i = 0; i <= tempContacts.size() ; i++) {
                Contact<> tempContact = tempContacts.get(i);
                tempContact.npo02__Household_Naming_Order__c.get(i) = tempContacts.get(i);
            }    */
            
        } //end of Trigger.new
    }

ERROR MESSAGE:

npsp.TDTM_Contact: execution of AfterInsert caused by: System.DmlException: Update failed. First exception on row 0 with id 0018A00000akD30QAE; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, HHNamingOrdering: execution of BeforeUpdate caused by: System.TypeException: Invalid integer: [Contact (Id:0038A00000XQthOQAT, RecordTypeId:012A0000000HmAjIAK, AccountId:0018A00000akD30QAE, Gender__c:Female, npo02__Household_Naming_Order__c:0, RecordType:[RecordType (Id:012A0000000HmAjIAK, Name:Parent)], Account:[Account (Id:0018A00000akD30QAE, npe01__One2OneContact__c:0038A00000XQthOQAT)])] (): [] (npsp)

2
  • Welcome to SFSE! Could you please edit your post to include complete text and stack trace of the error you are receiving?
    – David Reed
    Commented Dec 21, 2020 at 22:26
  • Error message added to original post. Curious if you have any advice. Many thanks! Commented Dec 22, 2020 at 6:48

1 Answer 1

0

Here's the proximate cause:

            tempContact.npo02__Household_Naming_Order__c = Integer.valueOf(tempContacts.get(i));

tempContext.get(i) is a Contact record. You can't convert a Contact record to an integer. I think what you meant was probably just i.

This trigger is going to cause you a lot of other problems, though, because it is not bulkified. Very quickly you will get into trouble with limits-related exceptions, especially in an NPSP context. I would strongly recommend completing the Bulk Apex Triggers module on Trailhead to get started with bulkification patterns.


Fully bulkifying this trigger will be perhaps more challenging than you might imagine. You can reduce the SOQL consumption to only one query, but you'll have to write more code to post-process the records in Apex to get the ordering you want.

Here's what I would suggest as a pattern for this trigger, in loose pseudocode. Note that this suggests also writing a wrapper class for Contact so that you can run a single SOQL query for related Contacts and then sort them in Apex. I'm omitting that here for brevity, but you can get some more information by searching SFSE and other resources for the Comparable interface.

// Run one Account query with a Contact subquery
List<Account> accounts = [
    SELECT Id,
           (SELECT Id, RecordType.Name, Age__c, Gender__c, npo02__Household_Naming_Order__c
            FROM Contacts)
    FROM Account
    WHERE Id IN :Trigger.newMap.keySet()
];
List<Contact> contactsToUpdate = new List<Contact>();

// Iterate over Accounts
for (Account a : accounts) {
    // Build a list of wrapper classes for the Account's Contacts.
    List<ContactWrapper> wrappers = new List<ContactWrapper>();
    for (Contact c : a.Contacts) {
         wrappers.add(new ContactWrapper(c));
    }
    // Sort the list of wrappers by your custom logic (defined in wrapper class)
    wrappers.sort();

    // Iterate over wrappers. Use the sort order to set the household naming order,
    // while accumulating a list of Contact sObjects to update.
    for (Integer i = 0; i < wrappers.size(); i++) {
         contactsToUpdate.add(
             new Contact(
                 Id = wrappers.get(i).contactId,
                 npo02__Household_Naming_Order__c = i
             )
         );
    }
}
// Update all Contacts at once.
update contactsToUpdate;

Again, that's a moderate amount of work to flesh out; you'd also need to build the wrapper class that implements Comparable to sort the list of Contacts for each Account. But it gets you down to exactly one SOQL and exactly one DML per trigger invocation.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .