[SalesForce] Update parent object on child object update/insert

I have opportunities that are linked by a related lookup to contacts. I would like a field on the contact to list all unique "program types" on opportunities. Thus, when an opportunity is updated or inserted, I would like the contact to iterate over its opportunities and pull up in a list all different "program types".

I have managed to get this done using execute anonymous in the dev console:

set<id> myContacts = new set<ID>();
list<opportunity> myOpps = new List<opportunity>();
list<contact> ContactObj = new list<contact>();
set<string> progTypes = new set<string>();
list<contact> toUpdate = new list<contact>();
myContacts.add('003e000000KpGmb');

myOpps = [select id, vested_org_user__c, program_type__c from opportunity where vested_org_user__c in : myContacts];
ContactObj = [select id, all_program_types__c from contact where id in : myContacts];
for (contact c: ContactObj) {
    progtypes.clear();
    for (opportunity o: myOpps) {
        if (o.Vested_org_User__c == c.id) {
            if (progTypes.contains(o.program_type__c)){

            } else {
                progtypes.add(o.program_type__c);
            }
        }
    }
    system.debug(progTypes);
    system.debug(JSON.serialize(progTypes));
    c.all_program_types__c = JSON.serialize(progTypes);
    update c;
}

However, when I try to put this into a trigger, nothing happens. I know that this execute anonymous code is not bulkified. Here is the trigger code:

trigger ProgType on Opportunity (after insert, after update) {
    set<id> myContacts = new set<ID>();
    list<opportunity> myOpps = new List<opportunity>();
    list<contact> ContactObj = new list<contact>();
    set<string> progTypes = new set<string>();
    list<contact> toUpdate = new list<contact>();

    for (opportunity o : Trigger.old) {
        if (o.recordtypeid == '012a00000018Esx') {
            myContacts.add(o.Vested_org_ID__c);
        }
    }
    myOpps = [select id, vested_org_user__c, program_type__c from opportunity where vested_org_user__c in : myContacts];
    ContactObj = [select id, all_program_types__c from contact where id in : myContacts];
    for (contact c: ContactObj) {
        progtypes.clear();
        for (opportunity o: myOpps) {
            if (o.Vested_org_User__c != null){
                if (o.Vested_org_User__c == c.id) {
                    if (progTypes.contains(o.program_type__c)){

                    } else {
                        progtypes.add(o.program_type__c);
                    }
                }
            }
        }
        Contact x = new contact();
        x.id = c.id;
        x.all_program_types__c = JSON.serialize(progTypes);
        toUpdate.add(x);
    }
    update toUpdate;}

I cannot fathom why one works and the other doesn't. All I know is that when I hard-code the opportunity I would like to trigger this whole thing it works…when I try to catch the opportunity using the after update trigger, it doesn't seem to work. Any help?

Thanks!

Best Answer

The trigger appears to be using the wrong field, based on the field name (and observed patterns through the rest of the code). It should be "Vested_Org_User__c" instead of "Vested_Org_Id__c".

As near as I can tell, you want your code to look like this:

trigger ProgType on Opportunity (after insert, after update, after delete, after undelete) {
    Set<Id> contactIds = new Set<Id>();
    if(Trigger.new != null) {
        for(Opportunity record: Trigger.new) {
            if(record.RecordTypeId == '012...') {
                contactIds.add(record.Vested_Org_User__c);
            }
        }
    }
    if(Trigger.old != null) {
        for(Opportunity record: Trigger.old) {
            if(record.RecordTypeId == '012...') {
                contactIds.add(record.Vested_Org_User__c);
            }
        }
    }
    contactIds.remove(null);
    Map<Id, Set<String>> programs = new Map<Id, Set<String>>();
    for(Opportunity record: [SELECT Vested_Org_User__c, Program_Type__c FROM Opportunity WHERE Vested_Org_User__c in :contactIds]) {
        if(programs.containsKey(record.Vested_Org_User__c)) {
            programs.get(record.Vested_Org_User__c).add(record.Program_Type__c);
        } else {
            programs.put(record.Vested_Org_User__c, new Set<String> { record.Program_Type__c });
        }
    }
    Contact[] contacts = new Contact[0];
    for(Id contactId: contactIds) {
        contacts.add(new Contact(Id=contactId, All_Program_Types__c=programs.containsKey(contactId)?JSON.serialize(programs.get(contactId)):null));
    }
    update contacts;
}

Standard optimizations are done here:

  • Use Trigger.new and Trigger.old to make sure you don't miss any contacts.
  • Use Maps and Sets to reduce overall usage, especially versus nested for loops.
  • Don't use queries when they're not needed (no need to query the contacts, as you already have their ID values).
  • Sets automatically deduplicate, so no need to check if the value exists first.
Related Topic