2

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!

2
  • 1
    Does Vested_Org_Id__c actually contain a contact ID? The name suggests it might be account ID or some such.
    – sfdcfox
    Jul 8, 2015 at 21:41
  • You'd be surprised how easy it is to use the wrong field.
    – sfdcfox
    Jul 8, 2015 at 21:56

1 Answer 1

3

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.
0

You must log in to answer this question.

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