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!

  • 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 '15 at 21:41
  • You'd be surprised how easy it is to use the wrong field. – sfdcfox Jul 8 '15 at 21:56
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.

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.